使用Scroller實現彈性滑動
阿新 • • 發佈:2018-12-10
scrollTo、scrollBy
View內部為了實現滑動提供了這兩個方法,但是使用這兩個方法滑動的效果是瞬間的不夠平滑,如何實現View的彈性滑動呢?這正是本博文討論的主題。另外這兩個函式滑動的是View的內容不是View本身。比如對於普通View好比TextView其內容就是文字,ImageView的內容則是drawable物件,採用這兩種方法滑動的時候其實分別滑動的是文字及drawable物件,對於ViewGroup採用這兩種方法滑動的時候則是對其子元素的滑動。所以想要使用scrollTo、scrollBy方法實現拖動View(指的是普通的View不包含ViewGroup)的效果必須在View外面在包一層ViewGroup。Scroller類
看到第9行,首先在SmoothScrollView 內部建立一個Scroller物件,第13行的smoothScroll方法是實現SmoothScrollView的平滑滑動,可以看到實現平滑滑動首先呼叫第18行Scroller的startScroll方法來設定滑動引數,下文會分析這個方法,這裡先放一放。然後在第20行呼叫invalidate方法,這個方法會導致SmoothScrollView重繪,從而呼叫draw方法之後又會呼叫computeScroll方法,在第24行可以看到這裡重寫了computeScroll方法,因此呼叫invalidate方法最終會導致computeScroll方法被呼叫。第27~29行呼叫Scroller的computeScrollOffeset方法並判斷是否滑動結束,computeScrollOffset是如何判斷滑動結束的呢?這裡也先放一放下文在分析。如果滑動未結束,執行第33行呼叫scrollTo滑動SmoothScrollView至此刻目的座標,然後遞迴呼叫invalidate方法。public class SmoothScrollView extends LinearLayout{ Scroller mScroller ; int startX; int startY; public SmoothScrollView(Context context, AttributeSet attrs) { super(context, attrs); //建立Scroller例項 mScroller = new Scroller(context); } public void smoothScroll(int dx,int dy,int duration){ //獲取滑動起點座標 startX = getScrollX(); startY = getScrollY(); //設定滑動引數 mScroller.startScroll(startX,startY,dx,dy,duration); //重新繪製View invalidate(); } @Override public void computeScroll() { // TODO Auto-generated method stub super.computeScroll(); boolean flag = mScroller.computeScrollOffset(); //遞迴終止條件:滑動結束 if(flag == false){ return; }else{ //mScroller.getCurrX(),mScroller.getCurrY()記錄的是此刻要滑動達到的目標座標 scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); } //遞迴呼叫 invalidate();//或者postInvalidate() } }
以下是對Scroller幾個方法的分析:
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;
//mDuration表示的是整個滑動持續的時間
mDurationReciprocal = 1.0f / (float) mDuration;
}
從以上程式碼可以看到startScroll方法其實根本沒有滑動View只是對滑動引數進行設定。往下再來看看computeScrollOffset方法,computeScrollOffset返回true則表示滑動還沒結束返回false表示滑動結束,它的實現如下:
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
//判斷此刻是否在有效滾動週期內
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
//當前時刻滑動偏移量所佔份額
float x = (float)timePassed * mDurationReciprocal;
if (mInterpolator == null)
x = viscousFluid(x);
else
x = mInterpolator.getInterpolation(x);
//獲取當前時刻要滾動到的位置
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
float timePassedSeconds = timePassed / 1000.0f;
float distance = (mVelocity * timePassedSeconds)
- (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);
mCurrX = mStartX + Math.round(distance * mCoeffX);
// Pin to mMinX <= mCurrX <= mMaxX
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY + Math.round(distance * mCoeffY);
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
看到這我們應該明白
SmoothScrollView
是如何實現讓自己平滑滑動的呢?實際上正真讓SmoothScrollView
產生平滑滑動的並非是Scroller而是
SmoothScrollView
自己,是
SmoothScrollView
自己多次呼叫了自己的s
crollerTo
方法並且每次滑動一小步從而實現平滑滑動,而
Scroller
類乾的事只是輔助
SmoothScrollView
計算每一次小滑動要到達的目標座標,而實現多次呼叫了自己的scrollerTo方法這裡沒有使用迴圈體則是利用程式設計技巧“遞迴呼叫”invalidate方法達到多次呼叫scrollTo方法的目的從而實現平滑滑動。