Android實現(xiàn)View滑動的幾種方式
什么是View?實現(xiàn)View滑動的方式有哪些?
1. 關于View我們需要知道的
(1)什么是View?
Android中的View類是所有UI控件的基類(Base class),也就是說我們平時所有到的各種UI控件,比如Button、ImagView等等都繼承自View類。LinearLayout、FrameLayout等布局管理器的直接父類是ViewGroup,而ViewGroup也有View類派生??偟膩碚f,View是對UI控件的抽象,它代表了屏幕上的一個矩形區(qū)域。通過繼承View,并重寫相應方法,我們就能夠實現(xiàn)具有各種外觀及行為的UI控件。Button等控件我們之所以能夠直接拿來即用,是因為Google已經幫我們完成了繼承View并重寫方法的工作。
(2)View的位置
View在屏幕上的位置由它的以下四個參數(shù)所決定:
top:View的左上角的縱坐標,對應著View類中的成員變量mTop,可由getTop方法獲得;
left:View的左上角的橫坐標,對應著View類中的成員變量mLeft,可由getLeft方法獲得;
bottom:View的右下角的縱坐標,對應著View類中的成員變量mBottom,可由getBottom方法獲得;
right:View的右下角的橫坐標,對應著View類中的成員變量mRight,可由getRight方法獲得。
注意,以上的坐標都是相對于父View來說的,也就是說,坐標都是相對坐標,因為子View的布局是由父View來完成的。如下圖所示:

有了這四個參數(shù),計算View的寬高就很容易了:width = right - left;height = bottom - top。關于View還有兩個參數(shù)需要我們注意:translationX代表View平移的水平距離,translationY代表View平移的豎直距離;x、y分別為View的左上角的橫縱坐標。View若經過了平移,改變的是它的x、y(代表當前View的左上角位置),它的四個位置參數(shù)代表了View的原始位置信息,是始終不變的。View在平移的過程中始終滿足如下關系:
x = left + translationX; y = top + translationY。
2. 實現(xiàn)View滑動的幾種方式
我們在使用View的過程中,經常需要實現(xiàn)View的滑動效果。比如ListView、跟隨手指而移動的自定義View等等,前者的滑動效果是SDK為我們提供的,而對于我們自定義View的滑動效果就需要我們自己來實現(xiàn)。下面我們來詳細介紹以下實現(xiàn)View滑動的幾種方式。
(1)使用scrollTo/scrollBy實現(xiàn)View的滑動
實現(xiàn)滑動的最樸素直接的方式就是使用View類自帶的scrollTo/scrollBy方法了。scrollBy方法是滑動指定的位移量,而scrollTo方法是滑動到指定位置。這兩個方法的源碼如下:
/**
* 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;
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
invalidate();
}
}
}
/**
* 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);
}
通過以上代碼的33~35行我們可以看到,scrollBy方法內部也是調用了scrollTo方法來實現(xiàn)。以上源碼中我們注意到了mScrollX和mScrollY成員變量,前者是View的左邊緣減去View的內容的左邊緣,后者是View的上邊緣減去View的內容的上邊緣。示意圖如下:

上圖中,黑色邊框代表View在屏幕上對應的矩形區(qū)域,藍色邊框代表View的內容。在上圖中,我們調用scrollTo/scrollBy把View向右滾動了一定距離。實際上,調用scrollBy/scrollTo方法只能實現(xiàn)View的內容的滾動,而View的四個位置參數(shù)是保持不變的。想一下我們平常使用ListView時,滾動的就是ListView的內容,而ListView本身在屏幕上的位置是不變的。上圖中,黑色左邊(即View的左邊緣)減去藍色左邊(即View的內容的左邊緣)即可得到mScrollX。由此我們還可以知道,向右滾動時mScrollX負的,向左滾動時mScrollX是正的。同理我們可以知道,向下滾動時,mScrollY是負的,向上滾動時,mScrollY是正的。
經過以上的分析,我們了解到使用scrollTo/scrollBy方法實現(xiàn)View的滑動是很簡單直接的,那么簡單的背后有什么代價呢?代價就是滑動不是“彈性的”,彈性滑動指的是View的滑動應該是一個先加速再逐漸減速到停止的過程,這樣看起來很平滑,不會很突兀。scrollTo/scrollBy方法實現(xiàn)的滑動看起來就會很突兀,這樣的用戶體驗很不好。在解決這個問題之前,我們先來看看實現(xiàn)View滑動的其他方法。
(2)使用動畫來實現(xiàn)View的滑動
使用動畫來實現(xiàn)View的滑動主要通過改變View的translationX和translationY參數(shù)來實現(xiàn),使用動畫的好處在于滑動效果是平滑的。上面我們提到過,View的x、y參數(shù)決定View的當前位置,通過改變translationX和translationY,我們就可以改變View的當前位置。我們可以使用屬性動畫或者補間動畫來實現(xiàn)View的平移。
首先,我們先來看一下如何使用補間動畫來實現(xiàn)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方法即可。使用補間動畫實現(xiàn)View的滑動有一個缺陷,那就是移動的知識View的“影像”,這意味著其實View并未真正的移動,只是我們看起來它移動了而已。拿Button來舉例,假若我們通過補間動畫移動了一個Button,我們會發(fā)現(xiàn),在Button的原來位置點擊屏幕會出發(fā)點擊事件,而在移動后的Button上點擊不會觸發(fā)點擊事件。
接下來,我們看看如何用屬性動畫來實現(xiàn)View的平移。使用屬性動畫實現(xiàn)View的平移更加簡單,只需要以下一條語句:
ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start();
以上代碼即實現(xiàn)了使用屬性動畫把targetView在100ms內向右平移100px。使用屬性動畫的限制在于真正的屬性動畫只可以在Android 3.0+使用(一些第三方庫實現(xiàn)的兼容低版本的屬性動畫不是真正的屬性動畫),優(yōu)點就是它可以真正的移動View而不是僅僅移動View的影像。
經過以上的描述,使用屬性動畫實現(xiàn)View的滑動看起來是個不錯的選擇,而且一些View的復雜的滑動效果只有通過動畫才能比較方便的實現(xiàn)。
(3)通過改變布局參數(shù)來實現(xiàn)View的滑動
通過改變布局參數(shù)來實現(xiàn)View的滑動的思想很簡單:比如向右移動一個View,只需要把它的marginLeft參數(shù)增大,向其它方向移動同理,只需改變相應的margin參數(shù)。還有一種比較拐彎抹角的方法是在要移動的View的旁邊預先放一個View(初始寬高設為0)。然后比如我們要向右移動View,只需把預先放置的那個View的寬度增大,這樣就把View“擠”到右邊了。代碼示例如下:
MarginLayoutParams params = (MarginLayoutParams) mButton.getLayoutParams(); params.leftMargin += 100; mButton.requestLayout();
以上代碼即實現(xiàn)了把mButton向右滑動100px。通過改變布局參數(shù)來實現(xiàn)的滑動效果也不是平滑的。
(4)使用Scroller來實現(xiàn)彈性滑動
上面我們提到了使用scrollTo/scrollBy方法實現(xiàn)View的滑動效果不是平滑的,好消息是我們可以使用Scroller方法來輔助實現(xiàn)View的彈性滑動。使用Scroller實現(xiàn)彈性滑動的慣用代碼如下:
Scroller scroller = new Scroller(mContext);
private void smoothScrollTo(int dstX, int dstY) {
int scrollX = getScrollX();
int delta = dstX - scrollX;
scroller.startScroll(scrollX, 0, delta, 0, 1000);
invalidate();
}
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), scroller.getCurY());
postInvalidate();
}
}
我們來看一下以上的代碼。第4行中,我們獲取到View的mScrollX參數(shù)并存到scrollX變量中。然后在第5行計算要滑動的位移量。第6行調用了startScroll方法,我們來看看startScroll方法的源碼:
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
mViscousFluidScale = 8.0f;
mViscousFluidNormalize = 1.0f;
mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
}
從以上的源碼我們可以看到,startScroll方法中并沒有進行實際的滾動操作,而是把startX、startY、deltaX、deltaY等參數(shù)都保存了下來。那么究竟怎么實現(xiàn)View的滑動的呢?我們先回到Scroller慣用代碼。我們看到第7行調用了invalidate方法,這個方法會請求重繪View,這會導致View的draw的方法被調用,draw的方法內部會調用computeScroll方法。我們來看看第13行,調用了scrollTo方法,并傳入mScroller.getCurrX()和mScroller.getCurrY()方法作為參數(shù)。那么獲取到的這兩個參數(shù)是什么呢?這兩個參數(shù)是在第12行調用的computeScrollOffset方法中設置的,我們來看看這個方法中設置這兩個參數(shù)的相關代碼:
public boolean computeScrollOffset() {
...
int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.rounc(y * mDeltaY);
break;
...
}
}
return true;
}
以上代碼中第8行和第9行設置的mCurrX和mCurrY即為以上scrollTo的兩個參數(shù),表示本次滑動的目標位置。computeScrollOffset方法返回true表示滑動過程還未結束,否則表示結束。
通過以上的分析,我們大概了解了Scroller實現(xiàn)彈性滑動的原理:invaldate方法會導致View的draw方法被調用,而draw會調用computeScroll方法,因此重寫了computeScroll方法,而computeScrollOffset方法會根據(jù)時間的流逝動態(tài)的計算出很小的一段時間應該滑動多少距離。也就是把一次滑動拆分成無數(shù)次小距離滑動從而實現(xiàn)“彈性滑動”。
本文提到的所有三種滑動方式的完整demo
以上就是本文的全部內容,希望對大家學習Android軟件編程有所幫助。
- android 通過向viewpage中添加listview來完成滑動效果(類似于qq滑動界面)
- 解析Android中實現(xiàn)滑動翻頁之ViewFlipper的使用詳解
- Android利用ViewPager實現(xiàn)滑動廣告板實例源碼
- android配合viewpager實現(xiàn)可滑動的標簽欄示例分享
- Android中實現(xiàn)監(jiān)聽ScrollView滑動事件
- Android中實現(xiàn)水平滑動(橫向滑動)ListView示例
- Android 利用ViewPager實現(xiàn)圖片可以左右循環(huán)滑動效果附代碼下載
- Android手勢滑動實現(xiàn)ImageView縮放圖片大小
- Android ViewPager無限循環(huán)實現(xiàn)底部小圓點動態(tài)滑動
相關文章
Android 勇闖高階性能優(yōu)化之啟動優(yōu)化篇
在移動端程序中,用戶希望的是應用能夠快速打開。啟動時間過長的應用不能滿足這個期望,并且可能會令用戶失望。輕則鄙視你,重則直接卸載你的應用2021-10-10
Android ListView分頁功能實現(xiàn)方法
這篇文章主要為大家詳細介紹了Android ListView分頁功能的實現(xiàn)方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05
解析Android開發(fā)優(yōu)化之:軟引用與弱引用的應用
Java從JDK1.2版本開始,就把對象的引用分為四種級別,從而使程序能更加靈活的控制對象的生命周期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用,本篇文章重點介紹一下軟引用和弱引用2013-05-05

