滾來滾去,滾來滾去...Scroller完全解析


   

API:

 1 mScroller.getCurrX() //獲取mScroller當前水平滾動的位置
 2 mScroller.getCurrY() //獲取mScroller當前豎直滾動的位置
 3 mScroller.getFinalX() //獲取mScroller最終停止的水平位置
 4 mScroller.getFinalY() //獲取mScroller最終停止的豎直位置
 5 mScroller.setFinalX(int newX) //設置mScroller最終停留的水平位置,沒有動畫效果,直接跳到目標位置
 6 mScroller.setFinalY(int newY) //設置mScroller最終停留的豎直位置,沒有動畫效果,直接跳到目標位置
 7 
 8 //滾動,startX, startY為開始滾動的位置,dx,dy為滾動的偏移量, duration為完成滾動的時間
 9 mScroller.startScroll(int startX, int startY, int dx, int dy) //使用默認完成時間250ms
10 mScroller.startScroll(int startX, int startY, int dx, int dy, int duration)
11 
12 mScroller.computeScrollOffset() //返回值為boolean,true說明滾動尚未完成,false說明滾動已經完成。這是一個很重要的方法,通常放在View.computeScroll()中,用來判斷是否滾動是否結束。

 

Scroller和OverScroller這兩個類是AndroidUI框架下實現滾動效果最關鍵的類,ScrollView內部的實現也是使用的OverScroller,所以熟練的使用這兩個類的相關API,可以讓我們滿足大部分的開發需求。

    在View類里面,有兩個和滾動相關的方法,scrollTo()和scrollBy:這兩個方法可以實現View內容的移動,注意,是內容,不是位置!是移動,不是滾動!什么叫做內容呢?比如說一個TextView,如果使用scrollTo(),那么移動的是里面的文字,而不是位置,scrollBy()也是一樣的。那么為什么是移動,不是滾動呢?這是因為這兩個方法完成的都是瞬間完成,即瞬移,而不是帶有滾動過程的滾動,所以說,如果要實現效果比較好的滾動,光靠View自帶的方法還是不行滴,還是要Scrollers出馬~

    但是Scrollers並不是控制View進行滾動,包括內容或者位置,Scrollers只是一個控件移動軌跡的輔助計算類,如果你想滾,他能幫你計算什么時間應該滾到什么位置,但是滾不滾,全靠你自覺~所以說,滾動位置由Scrollers計算出來了,我們在什么時候滾呢?滾多少呢?這時候,就要View的一個回調函數computeScroll()出馬了。

 

Scroller這個類理解起來有一定的困難,剛開始接觸Scroller類的程序員可能無法理解Scroller和View系統是怎么樣聯系起來的。我經過自己的學習和實踐,對Scroller的用法和工作原理有了一定的理解,在這里和大家分享一下,希望大家多多指教。

      首先從源碼開始分析: 

    ViewGroup.java   

  1. @Override  
  2. protected void dispatchDraw(Canvas canvas) {  
  3.   
  4.             .......  
  5.   
  6.             .......  
  7.   
  8.             .......  
  9.   
  10.             .......  
  11.   
  12.             for (int i = 0; i < count; i++) {  
  13.             final View child = children[i];  
  14.             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)  
  15.   
  16.             {  
  17.                 more |= drawChild(canvas, child, drawingTime);  
  18.             }  
  19.   
  20.             .......  
  21.   
  22.             .......  
  23.   
  24.             .......  

 

再看看drawChild函數:

  1.  protected boolean drawChild(Canvas canvas, View child, long drawingTime) {  
  2.   
  3.             ................  
  4.   
  5.             ................  
  6.   
  7.            child.computeScroll();  
  8.   
  9.             ................  
  10.   
  11.             ................  
  12.   
  13. }  

 

看到這里,我想大家應該就明白了,在父容器重畫自己的孩子時,它會調用孩子的computScroll方法, 我們看看View里面的computeScroll()做了些什么 

[java]  view plain  copy
 
  1. /** 
  2.      * Called by a parent to request that a child update its values for mScrollX 
  3.      * and mScrollY if necessary. This will typically be done if the child is 
  4.      * animating a scroll using a {@link android.widget.Scroller Scroller} 
  5.      * object. 
  6.      */  
  7.     public void computeScroll() {  
  8.     }  


    duang!空的!不過沒事,看看注釋,就是說,如果我們用Scroller實現一個滾動動畫的時候,這個方法就會被調用: 被誰調用呢?parent,誰改變呢?child。所以一般來說,這個方法可以用來更新mScrollX和mScrollY,但是其實不光可以改變這些,我們還能做其他事情。 

    如果我們調用Scroller.startScroll(int startX, int startY, int dx, int dy),那么我們就可以在computeScroll()里面執行實際的操作了,就像下面這樣 

[java]  view plain  copy
 
  1. @Override  
  2.     public void computeScroll() {  
  3.   
  4.         // 先判斷mScroller滾動是否完成  
  5.         if (mScroller.computeScrollOffset()) {  
  6.             // 這里調用View的scrollTo()完成實際的滾動  
  7.             scrollTo( mScroller.getCurrX(), mScroller .getCurrY());  
  8.             // 必須調用該方法,否則不一定能看到滾動效果  
  9.             invalidate();  
  10.         }  
  11.         super.computeScroll();  
  12.     }  


    Scroller.computeScrollOffset方法是來判斷滾動過程是否完成的,如果沒有完成,就需要不停的scrollTo下去,所以在最后需要加一個invalidate(),這樣可以再次觸發computScroll,直到滾動已經結束。 

 

    其實說到這里,有的同學可能比較迷惑,OverScroller和Scroller有什么區別呢?

事實上,這兩個類都屬於Scrollers,Scroller出現的比較早,在API1就有了,OverScroller是在API9才添加上的,出現的比較晚,所以功能比較完善,Over的意思就是超出,即OverScroller提供了對超出滑動邊界的情況的處理,這兩個類80%的API是一致的,OverScroller比Scroller添加了一下幾個方法

    ☞ isOverScrolled()
    ☞ springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) 
    ☞ fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY)
    ☞ notifyHorizontalEdgeReached(int startX, int finalX, int overX)
    ☞ notifyVerticalEdgeReached(int startY, int finalY, int overY)

    從名字也能看出來,都是對Over功能的支持,其他的API都一樣,所以介紹通用API的時候,並不區分OverScroller和Scroller。 

    下面簡單介紹一下常用的API。

    ☞ computeScrollOffset()  這個就是來判斷當前的滑動動作是否完成的,用法很單一,就是在computeScroll()里面來做判斷滾動是否完成

    ☞ getCurrX(),getCurrY()  這個就是獲取當前滑動的坐標值,因為Scrollers只是一個輔助計算類,所以如果我們想獲取滑動時的時時坐標,就可以通過這個方法獲得,然后在computeScroll()里面調用   

    ☞ startScroll(int startX, int startY, int dx, int dy) 用來開始滾動,這個是很重要的一個觸發computeScroll()的方法,調用這個方法之后,我們就可以在computeScroll里面獲取滾動的信息,然后完成我們的需要。這個還有一個帶有滾動持續時間的重載函數,可以根據需求自由使用。特別要注意這四個參數,startX和startY是開始的坐標位置,正數左上,負數右下,dx、dy同理,當在computeScroll()獲取getCurrX()的時候,變化范圍就與這里的設置有關。 

    ☞ fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) 這個方法也很重要,如果你想實現滑動之后,布局能夠根據移動速度,慢慢減速的話,就需要用這個來實現,這里需要加速度的參數,我們可以通過VelocityTracker這個類來獲取,然后使用。 

 ☞ getFinalX(),getFinalY()   這個是用來獲取最終滑動停止時的坐標

    ☞ isFinished()  用來判斷滾動是否結束

 

 

【參考資料】

滾來滾去,滾來滾去...Scroller相關類使用大揭秘!!! -- 趙凱強 

Android Scroller完全解析,關於Scroller你所需知道的一切 -- 郭霖 

Android 帶你從源碼的角度解析Scroller的滾動實現原理 -- 夏安明


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM