1. 程式人生 > >詳細介紹scrollTo、scrollBy、Scroller方法的作用,從原始碼分析其區別

詳細介紹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()裡判斷滾動是否已經結束,如果還未結束,我們可以繼續滾動它。例如,在一定距離和規定時間內完成它的多次分解滾動動作,將多次滾動串在一起形成一次平滑的滾動,效果將大大提升。