Android的Scroller源代碼分析

首先先來實現一個Scroller滑動

1.新建一個View,給它畫上一個紅色的矩形,左定點座標是(100,100),並且在構造函數中初始化Scroller

public class MyView extends View {

    private Scroller scroller;

    private Paint paint = new Paint();

    public MyView(Context context) {
        super(context);
        scroller = new Scroller(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        scroller = new Scroller(context);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        scroller = new Scroller(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setColor(Color.RED);
        canvas.drawRect(100, 100, 300, 300, paint);
    }

2.重寫view的computeScroll()方法,系統繪製view的時候會在draw()方法調用computeScroll()。

@Override
    public void computeScroll() {
        super.computeScroll();
        if (scroller.computeScrollOffset()) {
           //通過Scroller來獲取當前的滾動值
            scrollTo(scroller.getCurrX(), scroller.getCurrY());
//重繪,會重新調用computeScroll() 不斷移動view
            invalidate();
        }
    }

3.寫一個方法開始移動view,裏面調用startScroll()方法

public void smoothScrollTo(int destX,  int destY) {
        int scrollX = getScrollX();
        int deltaX = destX - scrollX;
        scroller.startScroll(scrollX, 0, -deltaX, 0, 10000);
        invalidate();
    }

4.在佈局文件中引用,Activity加載,寫一個按鈕控制smoothScrollTo()方法

    public void startScroller(View view) {
        myView.smoothScrollTo(400, 100);
    }

5.點擊按鈕後,效果圖

2.gif

分析源代碼

1.構造函數,有三個構造方法,不過第一種用的最廣,第二種,傳入一個插值器,然後在第三種中判斷了一下插值器是否爲空,不爲空則使用默認的ViscousFluidInterpolator插值器


    public Scroller(Context context) {
        this(context, null);
    }


    public Scroller(Context context, Interpolator interpolator) {
        this(context, interpolator,
                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
    }


    public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
        mFinished = true;
        if (interpolator == null) {
          //判斷插值器是否爲空
            mInterpolator = new ViscousFluidInterpolator();
        } else {
            mInterpolator = interpolator;
        }
        mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
        mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
        mFlywheel = flywheel;

        mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
    }

2.主要方法

  • startScroll(),有4個參數的和5個參數的,4個參數本質上也是調用了5個參數的startScroll(),區別在4個參數的用了默認的duration。
    startX:開始滑動的起點x
    startY:開始滑動的起點y
    dx:滑動的距離x,使用時注意正負
    dy:滑動的距離y,使用時注意正負

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

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;
    }

startScroll()方法是用來保存傳進來的各種參數的,沒有用來具體開啓滑動的邏輯,而我們要使View滑動,startScroll()要用到invalidate()來使view進行重繪,從而調用draw()方法,這樣draw()方法裏的就會call到View中的computeScroll()方法。View中的computeScroll()方法是空實現,我們需要重寫它

3.重寫computeScroll()方法

@Override
    public void computeScroll() {
        super.computeScroll();
        if (scroller.computeScrollOffset()) {
            //滾動的邏輯
            scrollTo(scroller.getCurrX(), scroller.getCurrY());
          //接着重繪
            invalidate();
        }
    }

可以看到computeScroll()裏用到了scrollTo()以滾動View,接着調用invalidate()會重新call到View中的draw()方法,從而不斷的調用computeScroll()方法,使view一點一點的滑動,從而實現了平滑滾動。

4.其中if判斷框中的computeScrollOffset()是用來獲取當前位置的ScrollX和ScrollY的,如果返回true這說明滑動未結束

public boolean computeScrollOffset() {
        if (mFinished) {
            return false;
        }
        //動畫持續時間
        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
        //如果當前的動畫持續時間小於設置的滑動持續時間(其實就是當前view滾動還沒完的意思)
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:
                //通過插值器計算該時間段移動的距離mCurrX ,mCurrX 
                final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
                mCurrX = mStartX + Math.round(x * mDeltaX);
                mCurrY = mStartY + Math.round(x * mDeltaY);
                break;
            case FLING_MODE:
                ...//省略
        }
        else {
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }

5.總結整個過程就是

Scroller過程


end

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章