博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android自定义View-垂直滚动的TextView
阅读量:6325 次
发布时间:2019-06-22

本文共 5782 字,大约阅读时间需要 19 分钟。

其实要让TextView能够滚动,可以使用ScrollView/HorizontalScrollView或者设置ScrollingMovementMethod来实现。

点击查看:

下面自定义垂直滚动的TextView,主要是用来学习Scroller的使用。关于ScrollTextView的实现,可以看下面的介绍和源码。

package com.orgcent.demo.view; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.animation.DecelerateInterpolator; import android.widget.Scroller; import android.widget.TextView; /**  * 配置android:scrollbars="vertical",启用垂直滚动条  *  * 实现水平滚动就是修改mScrollX,可参考HorizontalScrollView  *  * 滚动原理: 在绘制前对画布进行偏移操作  *  * 下面是View的绘制机制:  * |- view.computeScroll()  --用来对mScrollX/Y进行修改。由于在绘制前调用,可调用invalite()来触发  * |- canvas.translate(-mScrollX,-mScrollY)  --偏移画布  * |- view.draw()  --绘制  *   * 上述内容可以在View.buildDrawingCache()或ViewGroup.dispatchDraw()->drawChild()中找到.直接查看方法名即可  *  * 滚动帮助类:  * Scroller --用来计算滚动后的偏移值.具体请参考ScrollView和HorizontalScrollView  * VelocityTracker --速度计算类。根据fling时的按下、抬起动作,计算滚动初速度  *  * ScrollTextView--流程解析:  * 1、onTouchEvent() --使用Scroller来计算滚动偏移值  * 2、重写computeScroll() --对View的mScrollY进行修改, 此处控制滚动范围  *  * 滚动范围:  * 最小值:0  * 最大值:所有文本高度+内边距-View高度。也就是超出屏幕的文本高度 */ public class ScrollTextView extends TextView {
private Scroller mScroller; private int mTouchSlop; private int mMinimumVelocity; private int mMaximumVelocity; private float mLastMotionY; private boolean mIsBeingDragged; private VelocityTracker mVelocityTracker; private int mActivePointerId = INVALID_POINTER; private static final int INVALID_POINTER = -1; public ScrollTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); initView(); } public ScrollTextView(Context context, AttributeSet attrs) {
super(context, attrs); initView(); } public ScrollTextView(Context context) {
super(context); initView(); } private void initView() {
final Context cx = getContext(); //设置滚动减速器,在fling中会用到 mScroller = new Scroller(cx,new DecelerateInterpolator(0.5f)); final ViewConfiguration configuration = ViewConfiguration.get(cx); mTouchSlop = configuration.getScaledTouchSlop(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); } /** * 此方法为最后机会来修改mScrollX,mScrollY. * 这方法后将根据mScrollX,mScrollY来偏移Canvas已实现内容滚动 */ @Override public void computeScroll() { super.computeScroll(); final Scroller scroller = mScroller; if(scroller.computeScrollOffset()) { //正在滚动,让view滚动到当前位置 int scrollY = scroller.getCurrY(); final int maxY = (getLineCount() * getLineHeight() + getPaddingTop() + getPaddingBottom()) - getHeight(); boolean toEdge = scrollY < 0 || scrollY > maxY; if(scrollY < 0) scrollY = 0; else if(scrollY > maxY) scrollY = maxY; /* *下面等同于: * mScrollY = scrollY; * awakenScrollBars(); //显示滚动条,必须在xml中配置。 * postInvalidate(); */ scrollTo(0, scrollY); if(toEdge) //移到两端,由于位置没有发生变化,导致滚动条不显示 awakenScrollBars(); } } public void fling(int velocityY) {
final int maxY = (getLineCount() * getLineHeight() + getPaddingTop() + getPaddingBottom()) - getHeight(); mScroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0, Math.max(0, maxY)); //刷新,让父控件调用computeScroll() invalidate(); } @Override public boolean onTouchEvent(MotionEvent ev) {
/* * 事件处理方式:先自己处理后交给父类处理。 * PS:方式不同,可能导致效果不同。请根据需求自行修改。 */ boolean handled = false; final int contentHeight = getLineCount() * getLineHeight(); if(contentHeight > getHeight()) {
handled = processScroll(ev); } return handled | super.onTouchEvent(ev); } private boolean processScroll(MotionEvent ev) {
boolean handled = false; if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); //帮助类,用来在fling时计算移动初速度 final int action = ev.getAction(); switch (action) {
case MotionEvent.ACTION_DOWN: {
if(!mScroller.isFinished()) {
mScroller.forceFinished(true); } mLastMotionY = ev.getY(); mActivePointerId = ev.getPointerId(0); mIsBeingDragged = true; handled = true; break; } case MotionEvent.ACTION_MOVE: {
final int pointerId = mActivePointerId; if(mIsBeingDragged && INVALID_POINTER != pointerId) {
final int pointerIndex = ev.findPointerIndex(pointerId); final float y = ev.getY(pointerIndex); int deltaY = (int) (mLastMotionY - y); if(Math.abs(deltaY) > mTouchSlop) { //移动距离(正负代表方向)必须大于ViewConfiguration设置的默认值 mLastMotionY = y; /* * 默认滚动时间为250ms,建议立即滚动,否则滚动效果不明显 * 或者直接使用scrollBy(0, deltaY); */ mScroller.startScroll(getScrollX(), getScrollY(), 0, deltaY, 0); invalidate(); handled = true; } } break; } case MotionEvent.ACTION_UP: {
final int pointerId = mActivePointerId; if(mIsBeingDragged && INVALID_POINTER != pointerId) {
final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) velocityTracker.getYVelocity(pointerId); if(Math.abs(initialVelocity) > mMinimumVelocity) {
fling(-initialVelocity); } mActivePointerId = INVALID_POINTER; mIsBeingDragged = false; if (mVelocityTracker != null) {
mVelocityTracker.recycle(); mVelocityTracker = null; } handled = true; } break; } } return handled; } }

测试DEMO: 

转: 

 

转载于:https://www.cnblogs.com/shanzei/archive/2012/03/28/2421179.html

你可能感兴趣的文章
PHP中实现函数重载
查看>>
白宫电子邮件系统疑被黑:第一夫人护照信息被曝光
查看>>
站在物联网风口,传感器产业弯道超车?
查看>>
SQL Server-聚焦EXISTS AND IN性能分析(十六)
查看>>
使用oschina的git服务器图文流程 (转)
查看>>
国内物联网平台初探(一) ——百度物接入IoT Hub
查看>>
asp.net mvc中配置路由默认值(Area中)
查看>>
Redis从单机到集群,一步步教你环境部署以及使用
查看>>
【note】EtherCAT Configurator 使用之主菜单介绍
查看>>
iOS获取当前城市
查看>>
浅谈数据库联合查询
查看>>
可视化机器学习工具软件的比较分析研究
查看>>
OpenCV矩形检测
查看>>
InnoDB Master Thread I/O Rate详解
查看>>
org.apache.axis2.AxisFault: unknown
查看>>
Transport scheme NOT recognized: [stomp]
查看>>
用户与磁盘
查看>>
Oracle 10g通过创建物化视图实现不同数据库间表级别的数据同步
查看>>
SIP业务基本知识
查看>>
fn project 试用之后的几个问题
查看>>