詳細介紹scrollTo、scrollBy、Scroller方法的作用,從原始碼分析其區別
· 介紹
今天,我們來講講自定義View的基本功,那就是對我們螢幕座標系的理解。本人畫了一張圖,咋們湊合看吧,應該不難看懂。
這裡做略微的說明,黑色(ViewGroup、父容器)、藍色(包含的子View)、橙色為手指觸控式螢幕幕的一點。為什麼要清楚這張圖呢?因為在自定義View中經常出現這幾個方法,想必你不熟悉的話,可能看程式碼的時候就會一頭霧水!
熟悉完之後,我們來了解一下檢視滾動的幾個重要方法,下面我來看一張動態圖,相信你看完已經明白了scrollTo、scrollBy是怎麼一回事了。
· 分析
請仔細看完上面的動態圖,你會發現scrollTo和scrollBy的區別了。我這裡簡單的說明一下scrollTo和scrollBy的區別。
scrollTo(int x,int y),這是一個以座標點為目的的滾動,指定它所移動的座標位置,但如果重複移動的座標未發生改變,你只能看到一次移動效果。
scrollTo的原始碼分析:這個函式不難看出,當我們傳入x,y時,它會與view之前所處的位置進行比較,如果座標相同的話,將不會產生滾動。如果座標不同,它內部會呼叫onScrollChanged()去滾動檢視。
/** * 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(); } } }
scrollBy(int disX,int disY),這是一個以偏移量為目的滾動,指定它所移動的偏移量,既然是偏移量便可以多次看到移動效果。
scrollBy原始碼:它內部是呼叫scrollTo()方法來實現自身的滾動的。不難看出它是根據當前的座標加上目標移動的座標,除非你傳入 0 ,否則它始終會產生滾動效果。
/** * 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); }
· 重點
無論是scrollTo或scrollBy,你會發現往左移動時,例如scrollBy(20,0),它的x軸座標是正的,但它卻是往左移動。如上圖我們的座標系x軸右邊才是增加嗎?那它上面又會往左移動呢?
其實,這個是這樣理解的。因為它對應的參考系不同,比如對於子View來說,它想把右邊螢幕外的一個物體移動到螢幕內顯示,那手勢應該是從右往左滑動。對於子View的座標而言,右邊View的座標一定比左邊View的大,所以scrollBy(20,0)裡面的X座標只有增加時才能顯示出右邊,那麼當前子View則只能是左移了。這個比較抽象,理解起來不容易。
那麼接下來我們看看Scroller這個類,其實它與scrollTo和scrollBy類似的效果。這個Scroller類封裝了一些滾動行為,可想而知功能上肯定豐富了許多。
它的基本使用方式:
private Scroller mScroller;
private void init(){
mScroller = new Scroller(context);
}
/**
* startX ,startY 起始的x,y座標
* dx ,dy 滾動距離
*/
private void scroll(){
mScroller.startScroll(int startX, int startY, int dx, int dy);
postInvalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(x, y);
postInvalidate();
}
}
它的滾動也是一瞬間就完成的,這樣看起來滾動效果會特別生硬。於是,我們可以覆蓋它的computeScroll()方法。在postInvalidate()是會呼叫onDraw()、computeScroll()方法,然後在computeScroll()裡判斷滾動是否已經結束,如果還未結束,我們可以繼續滾動它。例如,在一定距離和規定時間內完成它的多次分解滾動動作,將多次滾動串在一起形成一次平滑的滾動,效果將大大提升。