国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

深入理解Android中Scroller的滾動原理

2019-12-12 05:37:49
字體:
來源:轉載
供稿:網友

View的平滑滾動效果

什么是實現View的平滑滾動效果呢,舉個簡單的例子,一個View從在我們指定的時間內從一個位置滾動到另外一個位置,我們利用Scroller類可以實現勻速滾動,可以先加速后減速,可以先減速后加速等等效果,而不是瞬間的移動的效果,所以Scroller可以幫我們實現很多滑動的效果。

首先我們先來看一下Scroller的用法,基本可概括為“三部曲”:

1、創建一個Scroller對象,一般在View的構造器中創建:

public ScrollViewGroup(Context context) {  this(context, null);}public ScrollViewGroup(Context context, AttributeSet attrs) {  this(context, attrs, 0);}public ScrollViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  mScroller = new Scroller(context);}

2、重寫View的computeScroll()方法,下面的代碼基本是不會變化的:

@Overridepublic void computeScroll() {  super.computeScroll();  if (mScroller.computeScrollOffset()) {    scrollTo(mScroller.getCurrX(), mScroller.getCurrY());    postInvalidate();  }}

3、調用startScroll()方法,startX和startY為開始滾動的坐標點,dx和dy為對應的偏移量:

mScroller.startScroll (int startX, int startY, int dx, int dy);invalidate();

上面的三步就是Scroller的基本用法了。

那接下來的任務就是解析Scroller的滾動原理了。

而在這之前,我們還有一件事要辦,那就是搞清楚scrollTo()scrollBy()的原理。scrollTo()scrollBy()的區別我這里就不重復敘述了,不懂的可以自行google或百度。

下面貼出scrollTo()的源碼:

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();    }  }}

設置好mScrollXmScrollY之后,調用了onScrollChanged(mScrollX, mScrollY, oldX, oldY);  ,View就會被重新繪制。這樣就達到了滑動的效果。

下面我們再來看看scrollBy()  :

public void scrollBy(int x, int y) {  scrollTo(mScrollX + x, mScrollY + y);}

這樣簡短的代碼相信大家都懂了,原來scrollBy()內部是調用了scrollTo()的。但是scrollTo() / scrollBy()的滾動都是瞬間完成的,怎么樣才能實現平滑滾動呢。

不知道大家有沒有這樣一種想法:如果我們把要滾動的偏移量分成若干份小的偏移量,當然這份量要大。然后用scrollTo() / scrollBy()每次都滾動小份的偏移量。在一定的時間內,不就成了平滑滾動了嗎?沒錯,Scroller正是借助這一原理來實現平滑滾動的。

下面我們就來看看源碼吧!

根據“三部曲”中第一部,先來看看Scroller的構造器:

public Scroller(Context context, Interpolator interpolator, boolean flywheel) {  mFinished = true;  if (interpolator == null) {    mInterpolator = new ViscousFluidInterpolator();  } else {    mInterpolator = interpolator;  }  mPpi = context.getResources().getDisplayMetrics().density * 160.0f;  mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());  mFlywheel = flywheel;  mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning}

在構造器中做的主要就是指定了插補器,如果沒有指定插補器,那么就用默認的ViscousFluidInterpolator

我們再來看看Scroller的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;}

我們發現,在startScroll()里面并沒有開始滾動,而是設置了一堆變量的初始值,那么到底是什么讓View開始滾動的?我們應該把目標集中在startScroll()的下一句invalidate();身上。我們可以這樣理解:首先在startScroll()設置好了一堆初始值,之后調用了invalidate();讓View重新繪制,這里又有一個很重要的點,在draw()中會調用computeScroll()這個方法!

源碼太長了,在這里就不貼出來了。想看的童鞋在View類里面搜boolean draw(Canvas canvas, ViewGroup parent, long drawingTime)這個方法就能看到了。通過ViewGroup.drawChild()方法就會調用子View的draw()方法。而在View類里面的computeScroll()是一個空的方法,需要我們去實現:

/** * Called by a parent to request that a child update its values for mScrollX * and mScrollY if necessary. This will typically be done if the child is * animating a scroll using a {@link android.widget.Scroller Scroller} * object. */public void computeScroll() {}

而在上面“三部曲”的第二部中,我們就已經實現了computeScroll()  。首先判斷了computeScrollOffset() ,我們來看看相關源碼:

/** * Call this when you want to know the new location. If it returns true, * the animation is not yet finished. */ public boolean computeScrollOffset() {  if (mFinished) {    return false;  }  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.round(x * mDeltaY);      break;    case FLING_MODE:      final float t = (float) timePassed / mDuration;      final int index = (int) (NB_SAMPLES * t);      float distanceCoef = 1.f;      float velocityCoef = 0.f;      if (index < NB_SAMPLES) {        final float t_inf = (float) index / NB_SAMPLES;        final float t_sup = (float) (index + 1) / NB_SAMPLES;        final float d_inf = SPLINE_POSITION[index];        final float d_sup = SPLINE_POSITION[index + 1];        velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);        distanceCoef = d_inf + (t - t_inf) * velocityCoef;      }      mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;            mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));      // Pin to mMinX <= mCurrX <= mMaxX      mCurrX = Math.min(mCurrX, mMaxX);      mCurrX = Math.max(mCurrX, mMinX);            mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));      // 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;}

這個方法的返回值有講究,若返回true則說明Scroller的滑動沒有結束;若返回false說明Scroller的滑動結束了。再來看看內部的代碼:先是計算出了已經滑動的時間,若已經滑動的時間小于總滑動的時間,則說明滑動沒有結束;不然就說明滑動結束了,設置標記mFinished = true;  。而在滑動未結束里面又分為了兩個mode,不過這兩個mode都干了差不多的事,大致就是根據剛才的時間timePassed和插補器來計算出該時間點滾動的距離mCurrXmCurrY。也就是上面“三部曲”中第二部的mScroller.getCurrX()  , mScroller.getCurrY()的值。

然后在第二部曲中調用scrollTo()方法滾動到指定點(即上面的mCurrX, mCurrY)。之后又調用了postInvalidate(); ,讓View重繪并重新調用computeScroll()以此循環下去,一直到View滾動到指定位置為止,至此Scroller滾動結束。

其實Scroller的原理還是比較通俗易懂的。我們再來理清一下思路,以一張圖的形式來終結今天的Scroller解析:

總結

好了,本文介紹Android中Scroller的滾動原理的內容到這就結束了,如果有什么問題可以在下面留言。希望本文的內容對大家開發Android能有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 苏尼特右旗| 南开区| 乐陵市| 资溪县| 高陵县| 巧家县| 喀什市| 措美县| 宁德市| 瓦房店市| 抚顺市| 寿宁县| 衢州市| 琼结县| 聂荣县| 双牌县| 来凤县| 重庆市| 天等县| 日喀则市| 宜君县| 昔阳县| 丰都县| 定远县| 仁怀市| 抚州市| 千阳县| 姚安县| 青川县| 和田县| 会昌县| 二手房| 高邑县| 睢宁县| 子长县| 株洲县| 万州区| 易门县| 陆河县| 普格县| 思茅市|