Android之Scroller详解讲解-真正了解滚动处理

1.Scroller是什么?

Scroller本身不会去移动View,它是一个移动计算辅助类,用于跟踪控件滑动的轨迹,只相当于一个滚动轨迹的记录工具,最终还是通过View的scrollTo,scrollBy方法完成View的移动;

2.Scroller源码分析,如何实现滚动轨迹记录的呢?

再讲源码之前先了解两个重要的方法:

1)startScroll()

public void startScroll(int startX, int startY, int dx, int dy, int duration) {}

开始一个动画控制,由(startX,startY)在duration时间内前进(dx,dy)个单位,即达到偏移坐标为(startX+dx,startY+dy)

2)computeScrollOffset()

public boolean computeScrollOffset()

滑动过程中,根据当前已经消逝的时间计算当前偏移的坐标点,保存在mCurrX和mCurrY值中。

上面两个方法的源码如下:

public class Scroller  {

    private int mStartX; //水平方向,滑动时的起点偏移坐标
    private int mStartY; //垂直方向,滑动时的起点偏移坐标
    private int mFinalX; //滑动完成后的偏移坐标,水平方向
    private int mFinalY; //滑动完成后的偏移坐标,垂直方向

    private int mCurrX;  //滑动过程中,根据消耗时间计算出的当前的滑动偏移距离,水平方向
    private int mCurrY;  //滑动过程中,根据消耗时间计算出的当前的滑动偏移距离,垂直方向
    private int mDuration; //本次滑动的动画时间
    private float mDeltaX; //滑动过程中,在达到mFinalX前还需要滑动的距离,水平方向   
    private float mDeltaY; //滑动过程中,在达到mFinalX前还需要滑动的距离,垂直方向   

    public void startScroll(int startX, int startY, int dx, int dy) {
        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
    }
    
    /**
    *  开始一个动画控制,由(startX,startY)在duration时间内前进(dx,dy)个单位,即达到偏移坐标为(startX+dx,startY+dy)
    */
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        mMode = SCROLL_MODE;
        mFinished = false;
        mDuration = duration;
        mStartTime = AnimationUtils.currentAnimationTimeMillis();
        mStartX = startX;
        mStartY = startY;
        mFinalX = startX + dx;    //确定本次滑动完成后的偏移坐标
        mFinalY = startY + dy;
        mDeltaX = dx;
        mDeltaY = dy;
        mDurationReciprocal = 1.0f / (float) mDuration;
    }
        
        /**
        *    滑动过程中,根据当前已经消耗的时间计算当前偏移的坐标点,保存在mCurrX和mCurrY值中
        */
        public boolean computeScrollOffset() {
        if (mFinished) {    //已经完成了本次动画控制,直接返回为false
            return false;
        }

        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:
                final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
                mCurrX = mStartX + Math.round(x * mDeltaX);//计算出当前滑动的偏移位置,x轴
                mCurrY = mStartY + Math.round(x * mDeltaY);//计算出当前滑动的偏移位置,y轴
                break;
                ...
            }
        }
        else {
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }
}

Scroller类中最重要的两个方法就是startScroll()和computeScrollOffset(),当时Scroller类只是一个滑动计算辅助类,它的startScroll()和computeScrollOffset()方法中也只是对一些轨迹参数进行设置和计算,真正需要进行滑动还的通过View的scrollTo(),scrollBy()方法。为此,View中提供了computeScroll方法来控制这个滑动流程。computeScroll()方法会在绘制子视图的时候进行调用。源码如下:

//由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制
public void computeScroll(){} //空方法,自定义滑动功能的ViewGroup必须实现的方法体

因此Scroller类的基本使用流程可以总结如下:

(1)首先通过Scroller类的startScroll()开始一个滑动动画控制,里面进行了一些轨迹参数的设置和计算;

(2)在调用 startScroll()的后面调用invalidate();引起视图的重绘操作,从而触发ViewGroup中的computeScroll()被调用;

(3)在computeScroll()方法中,先调用Scroller类中的computeScrollOffset()方法,里面根据当前消耗时间进行轨迹坐标的计算,然后取得计算出的当前滑动的偏移坐标,调用View的scrollTo()方法进行滑动控制,最后也需要调用invalidate();进行重绘。 如下的一个简单代码示例:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int)event.getX();
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                break;
            case MotionEvent.ACTION_MOVE:
                int dx = mLastX - x;
                int oldScrollX = getScrollX();  //原来的偏移量
                int preScrollX = oldScrollX + dx; //本次滑动后的形成的偏移量
                if(preScrollX<0){
                    preScrollX = 0;
                    dx = preScrollX - oldScrollX;
                }
                if(preScrollX>(getChildCount()-1)*getWidth()){
                    preScrollX = (getChildCount()-1)*getWidth();
                    dx = preScrollX - oldScrollX;
                }
                mScroller.startScroll(mScroller.getFinalX(),mScroller.getFinalY(),d
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值