詳解實現Android中實現View滑動的幾種方式

來源:互聯網
上載者:User

標籤:

注: 本文提到的所有三種滑動方式的完整demo:ScrollDemo1. 關於View我們需要知道的(1)什麼是View?

    Android中的View類是所有UI控制項的基類(Base class),也就是說我們平時所有到的各種UI控制項,比如Button、ImagView等等都繼承自View類。LinearLayout、FrameLayout等布局管理器的直接父類是ViewGroup,而ViewGroup也有View類派生。總的來說,View是對UI控制項的抽象,它代表了螢幕上的一個矩形地區。通過繼承View,並重寫相應方法,我們就能夠實現具有各種外觀及行為的UI控制項。Button等控制項我們之所以能夠直接拿來即用,是因為Google已經幫我們完成了繼承View並重寫方法的工作。

(2)View的位置

    View在螢幕上的位置由它的以下四個參數所決定:

  • top:View的左上方的縱座標,對應著View類中的成員變數mTop,可由getTop方法獲得;
  • left:View的左上方的橫座標,對應著View類中的成員變數mLeft,可由getLeft方法獲得;
  • bottom:View的右下角的縱座標,對應著View類中的成員變數mBottom,可由getBottom方法獲得;
  • right:View的右下角的橫座標,對應著View類中的成員變數mRight,可由getRight方法獲得。

    注意,以上的座標都是相對於父View來說的,也就是說,座標都是相對座標,因為子View的布局是由父View來完成的。如所示:

 

     有了這四個參數,計算View的寬高就很容易了:width = right - left;height = bottom - top。關於View還有兩個參數需要我們注意:translationX代表View平移的水平距離,translationY代表View平移的豎直距離;x、y分別為View的左上方的橫縱座標。View若經過了平移,改變的是它的x、y(代表當前View的左上方位置),它的四個位置參數代表了View的原始位置資訊,是始終不變的。View在平移的過程中始終滿足如下關係:

    x = left + translationX; y = top + translationY。

 

2. 實現View滑動的幾種方式

    我們在使用View的過程中,經常需要實現View的滑動效果。比如ListView、跟隨手指而移動的自訂View等等,前者的滑動效果是SDK為我們提供的,而對於我們自訂View的滑動效果就需要我們自己來實現。下面我們來詳細介紹以下實現View滑動的幾種方式。

(1)使用scrollTo/scrollBy實現View的滑動

    實現滑動的最樸素直接的方式就是使用View類內建的scrollTo/scrollBy方法了。scrollBy方法是滑動指定的位移量,而scrollTo方法是滑動到指定位置。這兩個方法的源碼如下:

 1 /**  2    * Set the scrolled position of your view. This will cause a call to  3  4    * {@link #onScrollChanged(int, int, int, int)} and the view will be  5  6    * invalidated.  7  8    * @param x the x position to scroll to  9 10    * @param y the y position to scroll to 11 12    */  13 public void scrollTo(int x, int y) {  14       if (mScrollX != x || mScrollY != y) {  15           int oldX = mScrollX;  16           int oldY = mScrollY;  17           mScrollX = x;  18           mScrollY = y;  19           onScrollChanged(mScrollX, mScrollY, oldX, oldY);  20           if (!awakenScrollBars()) {  21               invalidate();  22           }  23       }  24 }  25 26 /** 27     * Move the scrolled position of your view. This will cause a call to 28     * {@link #onScrollChanged(int, int, int, int)} and the view will be 29     * invalidated. 30     * @param x the amount of pixels to scroll by horizontally 31     * @param y the amount of pixels to scroll by vertically 32 */  33 public void scrollBy(int x, int y) {  34       scrollTo(mScrollX + x, mScrollY + y);  35 }  

 

    通過以上代碼的33~35行我們可以看到,scrollBy方法內部也是調用了scrollTo方法來實現。以上源碼中我們注意到了mScrollX和mScrollY成員變數,前者是View的左邊緣減去View的內容的左邊緣,後者是View的上邊緣減去View的內容的上邊緣。如下:

    中,黑色邊框代表View在螢幕上對應的矩形地區,藍色邊框代表View的內容。在中,我們調用scrollTo/scrollBy把View向右滾動了一定距離。實際上,調用scrollBy/scrollTo方法只能實現View的內容的滾動,而View的四個位置參數是保持不變的。想一下我們平常使用ListView時,滾動的就是ListView的內容,而ListView本身在螢幕上的位置是不變的。中,黑色左邊(即View的左邊緣)減去藍色左邊(即View的內容的左邊緣)即可得到mScrollX。由此我們還可以知道,向右滾動時mScrollX負的,向左滾動時mScrollX是正的。同理我們可以知道,向下滾動時,mScrollY是負的,向上滾動時,mScrollY是正的。

    經過以上的分析,我們瞭解到使用scrollTo/scrollBy方法實現View的滑動是很簡單直接的,那麼簡單的背後有什麼代價呢?代價就是滑動不是“彈性的”,彈性滑動指的是View的滑動應該是一個先加速再逐漸減速到停止的過程,這樣看起來很平滑,不會很突兀。scrollTo/scrollBy方法實現的滑動看起來就會很突兀,這樣的使用者體驗很不好。在解決這個問題之前,我們先來看看實現View滑動的其他方法。

 

(2)使用動畫來實現View的滑動

    使用動畫來實現View的滑動主要通過改變View的translationX和translationY參數來實現,使用動畫的好處在於滑動效果是平滑的。上面我們提到過,View的x、y參數決定View的當前位置,通過改變translationX和translationY,我們就可以改變View的當前位置。我們可以使用屬性動畫或者補間動畫來實現View的平移。

    首先,我們先來看一下如何使用補間動畫來實現View的平移。補間動畫資源定義如下(anim.xml):

<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"    android:fillAfter="true">    <translate        android:duration="100"        android:fromXDelta="0"        android:fromYDelta="0"        android:interpolator="@android:anim/linear_interpolator"        android:toXDelta="100"        android:toYDelta="100"/></set>

 

 

    然後在onCreat方法中調用startAnimation方法即可。使用補間動畫實現View的滑動有一個缺陷,那就是移動的知識View的“影像”,這意味著其實View並未真正的移動,只是我們看起來它移動了而已。拿Button來舉例,假若我們通過補間動畫移動了一個Button,我們會發現,在Button的原來位置點擊螢幕會出發點擊事件,而在移動後的Button上點擊不會觸發點擊事件。

    接下來,我們看看如何用屬性動畫來實現View的平移。使用屬性動畫實現View的平移更加簡單,只需要以下一條語句:

ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start();

 

    以上代碼即實現了使用屬性動畫把targetView在100ms內向右平移100px。使用屬性動畫的限制在於真正的屬性動畫只可以在Android 3.0+使用(一些第三方庫實現的相容低版本的屬性動畫不是真正的屬性動畫),優點就是它可以真正的移動View而不是僅僅移動View的影像。

   經過以上的描述,使用屬性動畫實現View的滑動看起來是個不錯的選擇,而且一些View的複雜的滑動效果只有通過動畫才能比較方便的實現。

 

(3)通過改變布局參數來實現View的滑動

    通過改變布局參數來實現View的滑動的思想很簡單:比如向右移動一個View,只需要把它的marginLeft參數增大,向其它方向移動同理,只需改變相應的margin參數。還有一種比較拐彎抹角的方法是在要移動的View的旁邊預先放一個View(初始寬高設為0)。然後比如我們要向右移動View,只需把預先放置的那個View的寬度增大,這樣就把View“擠”到右邊了。程式碼範例如下:

MarginLayoutParams params = (MarginLayoutParams) mButton.getLayoutParams();params.leftMargin += 100;mButton.requestLayout();

 

    以上代碼即實現了把mButton向右滑動100px。通過改變布局參數來實現的滑動效果也不是平滑的。

 

(4)使用Scroller來實現彈性滑動

    上面我們提到了使用scrollTo/scrollBy方法實現View的滑動效果不是平滑的,好訊息是我們可以使用Scroller方法來輔助實現View的彈性滑動。使用Scroller實現彈性滑動的慣用代碼如下:

 1 Scroller scroller = new Scroller(mContext); 2  3 private void smoothScrollTo(int dstX, int dstY) { 4     int scrollX = getScrollX(); 5     int delta = dstX - scrollX; 6     scroller.startScroll(scrollX, 0, delta, 0, 1000); 7     invalidate(); 8 } 9 10 @Override11 public void computeScroll() {12     if (scroller.computeScrollOffset()) {13         scrollTo(scroller.getCurrX(), scroller.getCurY());14         postInvalidate();15     }16 }

 

    我們來看一下以上的代碼。第4行中,我們擷取到View的mScrollX參數並存到scrollX變數中。然後在第5行計算要滑動的位移量。第6行調用了startScroll方法,我們來看看startScroll方法的源碼:

 1 public void startScroll(int startX, int startY, int dx, int dy, int duration) {   2     mMode = SCROLL_MODE;   3     mFinished = false;   4     mDuration = duration;   5     mStartTime = AnimationUtils.currentAnimationTimeMillis();   6     mStartX = startX;   7     mStartY = startY;   8     mFinalX = startX + dx;   9     mFinalY = startY + dy;  10     mDeltaX = dx;  11     mDeltaY = dy;  12     mDurationReciprocal = 1.0f / (float) mDuration;  13     14     mViscousFluidScale = 8.0f;  15    16     mViscousFluidNormalize = 1.0f;  17     mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);  18 }  

 

    從以上的源碼我們可以看到,startScroll方法中並沒有進行實際的滾動操作,而是把startX、startY、deltaX、deltaY等參數都儲存了下來。那麼究竟怎麼實現View的滑動的呢?我們先回到Scroller慣用代碼。我們看到第7行調用了invalidate方法,這個方法會請求重繪View,這會導致View的draw的方法被調用,draw的方法內部會調用computeScroll方法。我們來看看第13行,調用了scrollTo方法,並傳入mScroller.getCurrX()和mScroller.getCurrY()方法作為參數。那麼擷取到的這兩個參數是什麼呢?這兩個參數是在第12行調用的computeScrollOffset方法中設定的,我們來看看這個方法中設定這兩個參數的相關代碼:

 1 public boolean computeScrollOffset() { 2     ... 3     int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime); 4     if (timePassed < mDuration) { 5         switch (mMode) { 6             case SCROLL_MODE: 7                 final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal); 8                 mCurrX = mStartX + Math.round(x * mDeltaX); 9                 mCurrY = mStartY + Math.rounc(y * mDeltaY);10                 break;11         ...12         }13     }14     return true;15 }

 

    以上代碼中第8行和第9行設定的mCurrX和mCurrY即為以上scrollTo的兩個參數,表示本次滑動的目標位置。computeScrollOffset方法返回true表示滑動過程還未結束,否則表示結束。

    通過以上的分析,我們大概瞭解了Scroller實現彈性滑動的原理:invaldate方法會導致Viewdraw方法被調用,而draw會調用computeScroll方法,因此重寫了computeScroll方法,而computeScrollOffset方法會根據時間的流逝動態計算出很小的一段時間應該滑動多少距離。也就是把一次滑動拆分成無數次小距離滑動從而實現“彈性滑動”。

 

3. 參考資料

    《Android開發藝術探索》

     

以上敘述中若有不清晰或是不準確的地方,希望大家指出:) 

 

 

 

詳解實現Android中實現View滑動的幾種方式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.