Scroller平滑滾動

weixin_34138377發表於2016-05-16

標籤: Android


在我們做Android應用時,特別是自定義View時,經常要做一些動畫。我們主要是有View動畫和屬性動畫以及藉助Scoller類來實現View的平滑移動。現在主要來學習Scroller類的應用。


預備知識:
關於Scroller,我們首先要了解,我們滑動的是這個View的內容,而View本身位置是不能改變的。我們需要知道View的mScrollX和mScrollY這兩個屬性,這兩個屬性我們可以通過getScrollX()和getScrollY()分別得到。在滑動過程中,mScrollX表示View的左邊緣和View內容左邊緣在水平方向的距離。我們都知道View的位置,是由View的四個頂點的位置決定的。當View從左向右滑動時,mScrollX為負值,反之為正。當View從上向下滑動時,mScrollY為負值,反之為正。

現在我們來看Scroller的最基本的用法:
1.實現構造器

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

2.然後在需要滾動的時候,呼叫Scroller的startScroll()方法:
sample:在1000ms內水平滑至destX處。

  private void smoothScoller(int destX,int destY){
      int scrollX = getScrollX();
      int dx = destX - scrollX;
      mScoller.startScroll(scrollX,0,dx,0,1000);
      invalidate();
  }

3.實現View的computeScroll()方法:

   @Override
    public void computeScroll() {
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
            postInvalidate();
        }
    } 

這三步是Scroller的最基本的使用方法。

我們首先來看看Scroller的startScroll()方法:

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

很簡單的一個方法,這個方法其實並沒有做些關於動畫實現的方法,僅僅只是儲存了我們傳入的引數:

startX、startY: 滑動的起始點位置,
dx、dy: 將要滑動的偏移量
duration: 動畫將要持續的時間

既然startScroll()不是實現動畫的具體實現,那麼答案肯定在computeScroll()!我們繼續來看:

在startScroll()方法後面,呼叫了invalidate()方法,會強制View重新繪製,繪製過程中會呼叫View的draw()方法,在draw()方法裡,又回撥用computeScroll()。在View的原始碼裡,computeScroll()是個空方法,因此我們必須自己實現這個方法,上面即是我們自己實現的方法。在這個方法裡,Scroller會獲取View的當前位置getCurrX()和getCurrY(),然後通過scrollTo()來實現滑動。最後又呼叫postInvalidate()方法實現View的二次重繪,然後Scroller繼續獲取當前的位置,並通過scrollTo()來滑動到新位置,如此反覆,直到整個滑動過程結束。
在此過程中,還呼叫了computeScrollOffset()方法。這個方法是返回一個boolean型,主要變現為:如果當前動畫已完成,則返回false;如果沒有完成,則會一直返回true。

public boolean computeScrollOffset() {
        if (mFinished) {
            return false;
        }

        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:
                ...
                ...
                ...
                
                break;
            case FLING_MODE:
               
                ...
                ...
                
                if (mCurrX == mFinalX && mCurrY == mFinalY) {
                    mFinished = true;
                }

                break;
            }
        }
        else {
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }

總結:
Scroller其實並不會實現View的滑動,它只是儲存View的位置資訊,然後配合View的computeScroll()方法,不斷的重繪View,並修改View的滑動位置,並通過scrollTo()來滑動到新位置,這樣不斷的重複的小幅度滑動,由此組成了View的平滑滾動。

相關文章