1. 程式人生 > >View 的滑動實現之二(ScrollTo,ScrollBy和Scroller)

View 的滑動實現之二(ScrollTo,ScrollBy和Scroller)

在本篇文章的前面,我們講到了使用Layout的方法實現View的滑動今天給大家介紹一下使用ScrollTo,ScrollBy和Scroller來實現View的滑動。



一、ScrollTo,ScrollBy

在View中,系統專門提供了scrollTo和scrollBy兩種方式來改變View的位置,於是我們就可以通過這兩個方法實現View的滑動。

我們先來看一下這兩個方法的實現:

/**
 * Set the scrolled position of your view. This will cause a call to
 * {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated. * @param x the x position to scroll to * @param y the y position to scroll to */ public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX
, mScrollY, oldX, oldY); if (!awakenScrollBars()) { postInvalidateOnAnimation(); } } } /** * Move the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the amount of pixels to scroll by horizontally
* @param y the amount of pixels to scroll by vertically */ public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }

從上面的原始碼中可以看出,scrollBy實際上也是呼叫了scrollTo,兩者的區別也很容易發現,scrollTo是實現了所傳引數的絕對滑動,而scrollBy則是實現了所傳引數的相對滑動。就像英語中to和by的區別,應該不難理解。


好了,知道了上面這些,那我們就來寫一寫。

前面的onTouchEvent()程式碼相同,就不再贅述。

核心程式碼如下:

int offsetX=x-lastX;
int offsetY=y-lastY;
view.scrollBy(offsetX,offsetY);



但是!問題出現了,當我們拖動View的時候發現View並沒有移動,難道是我們程式碼寫錯了?

其實,我們的程式碼並沒有寫錯,而且View也確實移動了,只是它移動的並不是我們想要移動的東西。scrollTo,scrollBy方法移動的是View的Content,即讓View的內容移動,如果在ViewGroup中使用scrollTo,scrollBy方法,那麼移動的將是所有的子View,但如果在View中使用的話,那麼移動的將是View的內容。例如TextView,它的content是它的文字;Image View的content是他的Drawable物件。

通過上面的解釋,相信大家應該知道為什麼我們的View看著沒有移動了,現在我們更改一下上面的程式碼。

((View)getParent()).scrollBy(offsetX,offsetY);


讓我們再試一下,咦?發現view是移動了,怎麼感覺它是在亂動呢,並不像我們想象的那樣跟隨觸控點的移動而移動。

這裡大家需要理解一下檢視移動的一些知識。大家在理解這個問題的時候,不妨這樣想象手機螢幕是一箇中空的蓋板,下面是一個巨大的畫布,也就是我們想要顯示的檢視。當把這個蓋板蓋在畫布的某一處時,透過中空的矩形,我們看見了手機螢幕上顯示的檢視,而畫布上其他位置的檢視,則被蓋板蓋住而看不見。我們的檢視與這個例子非常類似,我們沒有看見檢視,並不代表它不存在,有可能只是在螢幕外邊而已。當呼叫scrollBy方法時,可以認為是外面的蓋板在移動。

通過上面的理解,我們驗證一下,我們使用scrollBy(20,10),偏移量均為X軸,Y軸上的正數,但是在螢幕的可視區域,可以看到View卻向X軸、Y軸的負方向移動。正好證明了上面的解釋。這是因為座標系選取的不同,而產生的效果。

所以,通過上面的驗證和理解,我們可以得出結論,如果想要實現跟隨手指而滑動的效果,那就必須使用負值的引數,程式碼如下:

int offsetX=x-lastX;
int offsetY=y-lastY;
((View)getParent()).scrollBy(-offsetX,-offsetY);

再去驗證一下,大家就可以發現效果與View的滑動之一中的效果一樣了,類似的,使用絕對座標時,也可以使用scrollTo()方法來實現這一效果。

二、Scroller

彈性滑動物件,用於實現View的彈性滑動,我們都知道,當時用View的scrollTo和scrollBy方法來進行滑動時,其過程時瞬間完成的,這個效果讓人感覺非常突兀,因此,scroller應運而生,通過scroller類可以實現平滑移動的效果,而不再是瞬間完成的了。

說起scroller的實現原理,我們不妨細細考慮一下,其實它與scrollTo和scrollBy方法實現子View的移動 的原理是基本相似的。雖說scrollBy方法是讓子View從某點瞬間移動到另一點,但是如果每次都是微笑的偏移量的話,就會形成一個平滑移動的效果。這個原理與動畫的實現原理是基本類似的,它利用的是人眼的視覺暫留特性。

scroller本身無法讓View彈性滑動,他需要和View的computeScroll方法配合使用。

步驟如下:

1.初始化Scroller

首先,通過它的構造方法建立一個Scroller物件:

Scroller mScroller=new Scroller(context);
2.重寫computeScroll方法,實現彈性滑動。

computeScroll是Scroller類的核心,系統在繪製的時候,會在draw()方法中呼叫該方法。這個方法實際上就是使用的scrollTo方法,再結合scroller物件,幫助獲取到當前的滾動值,然後通過不斷的瞬間移動一個小的舉例來實現整體上的平滑移動效果。

@Override
public void computeScroll(){
    super.computeScroll();
//判斷Scroller是否執行完畢
if(mScroller.computeScrollOffset){
        ((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();//通過重繪來不斷呼叫computeScroll()
}
}

Scroller 類提供了computeScrollOffset方法來判斷是否完成整個滑動,同時也提供了getCurrX()和getCurrY()方法來獲得當前滑動的座標。在上面的程式碼中,唯一需要注意的是postInvalidate()方法,因為只能夠在computeScroll方法中獲取滑動過程中的scroll X和scrollY座標,但是computeScroll方法是不會自動給呼叫的,只能通過invalidate->draw->computeScroll方法來間接呼叫computeScroll方法,所以需要使用postInvalidate或是Invalidate來迴圈間接呼叫computeScroll,直至mScroller.computeScrollOffset返回false,完成整個平滑移動過程。

最後,我們只需要呼叫startScroll開啟彈性滑動過程。

startScroll()方法有兩個過載方法:

public void startScroll(int startX,int startY, int dy, int duration)

public void startScroll(int startX,  int startY, int dx, int dy)

兩個的區別是可以指定滑動持續的事件。

現在我們來實現一個平滑的方法,程式碼如下:

private void smoothScrollTo(int destX,int destY){
    int scrollX=viewGroup.getScrollX();
    int deltaX=destX-scrollX;
    int scrollY=viewGroup.getScrollY();
    int deltaY=destY-scrollY;
//1000ms內滑向dest
mScroller.startScroll(scrollX,scrollY,-deltaX,-deltaY,1000);//ViewGroup的偏移量引數和ScrollBy一樣需要是負數
invalidate();//需要先進行重繪,從而開始呼叫computeScroll,開始迴圈彈性滑動過程
}
好了,今天的View的滑動就介紹到這裡,後面會給大家介紹使用動畫和屬性動畫來實現平滑移動效果。