zoukankan      html  css  js  c++  java
  • android自定义View垂直滚动的TextView

    其实要让TextView能够滚动,可以使用ScrollView/HorizontalScrollView或者设置ScrollingMovementMethod来实现。
    点击查看:android实现TextView垂直或水平滚动

    下面自定义垂直滚动的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: 点击查看

  • 相关阅读:
    MinGW GCC下sleep()函数问题
    memcached内存分配及回收初探
    fastcgi重启
    window wamp下xhprof的安装使用,Graphviz配置
    Bootstrap研究3-基础html元素
    sql之left join、right join、inner join的区别
    【Asp.net入门03】第一个ASP.NET 应用程序-创建ASP.NET项目
    【Asp.net入门02】搭建Asp.net开发环境
    【Asp.net入门01】动态网站基础知识
    Android Studio 安装在Windows10中的陷阱
  • 原文地址:https://www.cnblogs.com/shanzei/p/2421179.html
Copyright © 2011-2022 走看看