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
- @Override
- protected void dispatchDraw(Canvas canvas) {
- .......
- .......
- .......
- .......
- for (int i = 0; i < count; i++) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)
- {
- more |= drawChild(canvas, child, drawingTime);
- }
- .......
- .......
- .......
再看看drawChild函數:
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- ................
- ................
- child.computeScroll();
- ................
- ................
- }
看到這里,我想大家應該就明白了,在父容器重畫自己的孩子時,它會調用孩子的computScroll方法, 我們看看View里面的computeScroll()做了些什么
- /**
- * Called by a parent to request that a child update its values for mScrollX
- * and mScrollY if necessary. This will typically be done if the child is
- * animating a scroll using a {@link android.widget.Scroller Scroller}
- * object.
- */
- public void computeScroll() {
- }
duang!空的!不過沒事,看看注釋,就是說,如果我們用Scroller實現一個滾動動畫的時候,這個方法就會被調用: 被誰調用呢?parent,誰改變呢?child。所以一般來說,這個方法可以用來更新mScrollX和mScrollY,但是其實不光可以改變這些,我們還能做其他事情。
如果我們調用Scroller.startScroll(int startX, int startY, int dx, int dy),那么我們就可以在computeScroll()里面執行實際的操作了,就像下面這樣
- @Override
- public void computeScroll() {
- // 先判斷mScroller滾動是否完成
- if (mScroller.computeScrollOffset()) {
- // 這里調用View的scrollTo()完成實際的滾動
- scrollTo( mScroller.getCurrX(), mScroller .getCurrY());
- // 必須調用該方法,否則不一定能看到滾動效果
- invalidate();
- }
- super.computeScroll();
- }
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相關類使用大揭秘!!! -- 趙凱強