Android中scrollTo()和scrollBy()的区别以及Scroller源码解析

来源:互联网 发布:gre知乎 编辑:程序博客网 时间:2024/06/05 07:07

文章转自http://blog.csdn.net/crazy__chen/article/details/45896961。

滑动是我们在自定义控件时候经常遇见的难题,让新手们倍感困惑,这篇文章主要介绍Scroller类的源码,告诉打击这个到底有什么用,怎么使用它来控制滑动。另外,我还会结合一个简单的例子,来看一下这个类的应用。

要说明Scroller类,我们往往要从另外两个方法说起,一个是ScrollTo(),一个是ScrollBy()

这两个方法我们可以在View的源码看到,我们知道其实每个控件都有滚动条,只是有的我们将它隐藏,所以我们看不见

下面是ScrollTo方法

[java] view plain copy
  1. /** 
  2.    * Set the scrolled position of your view. This will cause a call to 
  3.    * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  4.    * invalidated. 
  5.    * @param x the x position to scroll to 
  6.    * @param y the y position to scroll to 
  7.    */  
  8.   public void scrollTo(int x, int y) {//滑动到的目标坐标  
  9.       if (mScrollX != x || mScrollY != y) {  
  10.           int oldX = mScrollX;//已经滑动到的X  
  11.           int oldY = mScrollY;//已经滑动到的Y  
  12.           mScrollX = x;  
  13.           mScrollY = y;  
  14.           onScrollChanged(mScrollX, mScrollY, oldX, oldY);//调用说明状态改变  
  15.           if (!awakenScrollBars()) {  
  16.               invalidate();//通知视图进行重绘  
  17.           }  
  18.       }  
  19.   }  
ScrollTo是一个public方法,说明我们可以在外部调用它,而正是我们调用了它,才能实现滑动,它的滑动效果是瞬间的,也就是说一步到位。

传进去的x,y是目的坐标,mScrollX,mScrollY是之前已经滑动到位置,调用这个函数以后,我们可以看到mScrollX,mScrollY被我们重新赋值,接下来会调用invalidate()进行重绘

那么滑动的本质是什么?根据动画移动的基本原理,对于一个控件来说,它的大小是有限的(例如我们可以自己设定,或者说被父控件所束缚)。所以我们绘图时会在这个有限的大小内进行绘制,但是控件的Canvas本质上是无限的,也就是一个控件的画布大小是无限,控件的大小就像一个窗口,我们只是通过这个窗口去看这块画布。

所以滑动的本质是,在窗口所见的画布部分,我们画些什么。

mScrollX,mScrollY起始是0,0,也就是说,窗口看见的,从画布的左上角开始的,然后我们改变两个值,例如100,100,窗口看见的,就是从100,100这个坐标开始绘制的东西(我们也可能没有在这块区域绘制东西,所以显示空白,因为画布是无限的)

说了这么多不知道大家有没有听懂,总之ScrollTo就是重新定义绘制起点坐标,从而实现滑动,由于绘制的一瞬间就开始的,所以ScrollTo造成的效果也是瞬间的

再来看看ScrollBy()方法

[java] view plain copy
  1. /** 
  2.     * Move the scrolled position of your view. This will cause a call to 
  3.     * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  4.     * invalidated. 
  5.     * @param x the amount of pixels to scroll by horizontally 
  6.     * @param y the amount of pixels to scroll by vertically 
  7.     */  
  8.    public void scrollBy(int x, int y) {  
  9.        scrollTo(mScrollX + x, mScrollY + y);  
  10.    }  
它里面调用了ScrollTo(),那样我们就好理解了。传入的参数是mScrollX+x,也就是说这次x是一个增量,所以scrollBy实现的效果就是,在原来的起始位置,偏移x距离,再重新绘制,这是ScrollTo()和ScrollBy()的重要区别。


OK,上面讲了这么多,我们知道了用ScrollTo()就可以实现滑动了,但是我们需要的不是这样的滑动,我们希望滑动是有过程的,要缓慢的滑,要有feeling。

做javascript的朋友可能会想到,我使用一个for循环,然后每次调用ScrollBy()移动一定距离,每次移动后,让进程沉睡一段时间,不久可以实现动画的效果了吗?

是的,这是一般的思维,开始这样我们会面临两个问题,一个是让主线程沉睡(这是万万不能),一个是假设我们要实现多样的滑动算法(先快后慢,先慢后快等),怎么办?

其实cpu每个一定时间就会从新绘制画布(如果控件需要重新绘制的话),所以我们不必要让主线程沉睡,至于后面一个问题确实值得考虑。


直接地说,解决办法就是Scroller类,很多人看到这个类名,会不知道它跟滑动有什么关系。下面说一下,下面这段是Scroller的本质,只要听懂了,这篇文章就算没有白看了。

Scroller不能让控件滑动,之所以不能,因为本质上使控件滑动的是scrollTo()和scrollBy()方法

根据上面实现动画的思想,我们有必要每次调用ScrollTo方法前,计算出的新的x,y值,而Scroller就是替我们计算这两个值的

我们可以设定Scroller的内部算法(用于实现滑动物理的效果,我们不需要了解具体算法内容),然后调用Scroller的startScroll方法,这方法传入起始坐标,目的坐标,和滑动完成所需的时间,一旦调用这个方法以后,我们就可以通过它的getCurrX(),getCurrY()方法获得新的x,y坐标(这个两个坐标,是通过内部算法得来的)。

所以我们每次获得新坐标,然后scrollTo就行了,就可以实现滑动了。那么我们每次什么时候滑动呢(按照什么频率?难道真的写for循环?),当然是cpu绘制的频率最合理。

View里面有一个computeScroll()方法,在每次draw之前会调用,我们可以在调用这个方法时滑动(这个方法其实就是为我们滑动准备的啊!)

[java] view plain copy
  1. @Override  
  2.     public void computeScroll() {  
  3.         }  

上面说的有点多,下面直接看scroller类源码

首先是一些属性,重要的我都有注释,没有注释的,是默认算法需要的,我们不必理睬

[java] view plain copy
  1. /** 
  2.      * 模式,有SCROLL_MODE和FLING_MODE 
  3.      */  
  4.     private int mMode;  
  5.     /** 
  6.      * 起始x方向偏移 
  7.      */  
  8.     private int mStartX;  
  9.     /** 
  10.      * 起始y方向偏移 
  11.      */  
  12.     private int mStartY;  
  13.     /** 
  14.      * 终点x方向偏移 
  15.      */  
  16.     private int mFinalX;  
  17.     /** 
  18.      * 终点y方向偏移 
  19.      */  
  20.     private int mFinalY;  
  21.   
  22.     private int mMinX;  
  23.     private int mMaxX;  
  24.     private int mMinY;  
  25.     private int mMaxY;  
  26.   
  27.     /** 
  28.      * 当前x方向偏移 
  29.      */  
  30.     private int mCurrX;  
  31.     /** 
  32.      * 当前y方向偏移 
  33.      */  
  34.     private int mCurrY;  
  35.     /** 
  36.      * 起始时间 
  37.      */  
  38.     private long mStartTime;  
  39.     /** 
  40.      * 滚动持续时间 
  41.      */  
  42.     private int mDuration;  
  43.     /** 
  44.      * 持续时间的倒数 
  45.      */  
  46.     private float mDurationReciprocal;  
  47.     /** 
  48.      * x方向应该滚动的距离,mDeltaX=mFinalX-mStartX 
  49.      */  
  50.     private float mDeltaX;  
  51.     /** 
  52.      * y方向应该滚动的距离,mDeltaY=mFinalY-mStartY 
  53.      */  
  54.     private float mDeltaY;  
  55.     /** 
  56.      * 是否结束 
  57.      */  
  58.     private boolean mFinished;  
  59.     /** 
  60.      * 插值器 
  61.      */  
  62.     private Interpolator mInterpolator;  
  63.     /** 
  64.      * 调速轮 
  65.      */  
  66.     private boolean mFlywheel;  
  67.   
  68.     private float mVelocity;  
  69.     /** 
  70.      * 默认滑动时间 
  71.      */  
  72.     private static final int DEFAULT_DURATION = 250;  
  73.     /** 
  74.      * 滑动模式 
  75.      */  
  76.     private static final int SCROLL_MODE = 0;  
  77.     /** 
  78.      * 猛冲模式 
  79.      */  
  80.     private static final int FLING_MODE = 1;  
  81.   
  82.     private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));  
  83.     private static float ALPHA = 800// pixels / seconds  
  84.     private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)  
  85.     private static float END_TENSION = 1.0f - START_TENSION;  
  86.     private static final int NB_SAMPLES = 100;  
  87.     private static final float[] SPLINE = new float[NB_SAMPLES + 1];  
  88.     /** 
  89.      * 减速率 
  90.      */  
  91.     private float mDeceleration;  
  92.     /** 
  93.      * pixels per inch 
  94.      */  
  95.     private final float mPpi;  

然后来看构造函数

[java] view plain copy
  1. /** 
  2.      * Create a Scroller with the specified interpolator. If the interpolator is 
  3.      * null, the default (viscous) interpolator will be used. Specify whether or 
  4.      * not to support progressive "flywheel" behavior in flinging. 
  5.      * 通过Ppi和滑动摩擦因素,计算减速率 
  6.      */  
  7.     public Scroller(Context context, Interpolator interpolator, boolean flywheel) {  
  8.         mFinished = true;  
  9.         mInterpolator = interpolator;  
  10.         mPpi = context.getResources().getDisplayMetrics().density * 160.0f;  
  11.         mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());  
  12.         mFlywheel = flywheel;  
  13.     }  
为控件创建scroller时,需要传入context,另外interpolator是插值器,不同的插值器实现不同的动画算法,如果我们不传,则使用scroller内部算法,接下面我就会看到

另外flywheel是flywheel,有版本的限制,一般也不会用到,也是用于滑动动画算法的实现

可以看到构造函数里面,只是做了一些设定,mFinished表示滑动是否结束

再来看很重要的startScroll()方法

[java] view plain copy
  1. /** 
  2.      * Start scrolling by providing a starting point and the distance to travel. 
  3.      * The scroll will use the default value of 250 milliseconds for the 
  4.      * duration. 
  5.      *  
  6.      * @param startX Starting horizontal scroll offset in pixels. Positive 
  7.      *        numbers will scroll the content to the left. 
  8.      * @param startY Starting vertical scroll offset in pixels. Positive numbers 
  9.      *        will scroll the content up. 
  10.      * @param dx Horizontal distance to travel. Positive numbers will scroll the 
  11.      *        content to the left. 
  12.      * @param dy Vertical distance to travel. Positive numbers will scroll the 
  13.      *        content up. 
  14.      * 使用默认滑动时间完成滑动 
  15.      */  
  16.     public void startScroll(int startX, int startY, int dx, int dy) {  
  17.         startScroll(startX, startY, dx, dy, DEFAULT_DURATION);  
  18.     }  
  19.   
  20.     /** 
  21.      * Start scrolling by providing a starting point and the distance to travel. 
  22.      *  
  23.      * @param startX Starting horizontal scroll offset in pixels. Positive 
  24.      *        numbers will scroll the content to the left. 
  25.      *        滑动起始X坐标 
  26.      * @param startY Starting vertical scroll offset in pixels. Positive numbers 
  27.      *        will scroll the content up. 
  28.      *        滑动起始Y坐标 
  29.      * @param dx Horizontal distance to travel. Positive numbers will scroll the 
  30.      *        content to the left. 
  31.      *        X方向滑动距离 
  32.      * @param dy Vertical distance to travel. Positive numbers will scroll the 
  33.      *        content up. 
  34.      *        Y方向滑动距离 
  35.      * @param duration Duration of the scroll in milliseconds. 
  36.      *            完成滑动所需的时间      
  37.      * 从起始点开始滑动一定距离 
  38.      */  
  39.     public void startScroll(int startX, int startY, int dx, int dy, int duration) {  
  40.         mMode = SCROLL_MODE;  
  41.         mFinished = false;  
  42.         mDuration = duration;  
  43.         mStartTime = AnimationUtils.currentAnimationTimeMillis();//获取当前时间作为滑动的起始时间  
  44.         mStartX = startX;  
  45.         mStartY = startY;  
  46.         mFinalX = startX + dx;  
  47.         mFinalY = startY + dy;  
  48.         mDeltaX = dx;  
  49.         mDeltaY = dy;  
  50.         mDurationReciprocal = 1.0f / (float) mDuration;  
  51.     }  

说它非常重要,但是内部却很简单,就是定义滑动的起始坐标,目的坐标和滑动时间,所以说根本没有实现滑动的效果

那么算法是在哪个方法里面调用的呢?

是computeScrollOffset()方法,这个方法返回false,说明滑动没有结束,但是它更重要的作用是,利用内部算法,起始坐标,目的坐标和滑动时间,从滑动开始到当前经过的时间,计算出新的坐标

所以说,我们在获取新的坐标前,一定要调用这个函数,这个函数的功能非常重要,而且并不简单!

[java] view plain copy
  1. /** 
  2.      * Call this when you want to know the new location.  If it returns true, 
  3.      * the animation is not yet finished.  loc will be altered to provide the 
  4.      * new location. 
  5.      * 调用这个函数获得新的位置坐标(滑动过程中)。如果它返回true,说明滑动没有结束。 
  6.      * getCurX(),getCurY()方法就可以获得计算后的值。 
  7.      */   
  8.     public boolean computeScrollOffset() {  
  9.         if (mFinished) {//是否结束  
  10.             return false;  
  11.         }  
  12.   
  13.         int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);//滑动开始,经过了多长时间  
  14.       
  15.         if (timePassed < mDuration) {//如果经过的时间小于动画完成所需时间  
  16.             switch (mMode) {  
  17.             case SCROLL_MODE:  
  18.                 float x = timePassed * mDurationReciprocal;  
  19.       
  20.                 if (mInterpolator == null)//如果没有设置插值器,利用默认算法  
  21.                     x = viscousFluid(x);   
  22.                 else//否则利用插值器定义的算法  
  23.                     x = mInterpolator.getInterpolation(x);  
  24.       
  25.                 mCurrX = mStartX + Math.round(x * mDeltaX);//计算当前X坐标  
  26.                 mCurrY = mStartY + Math.round(x * mDeltaY);//计算当前Y坐标  
  27.                 break;  
  28.             case FLING_MODE:  
  29.                 final float t = (float) timePassed / mDuration;  
  30.                 final int index = (int) (NB_SAMPLES * t);  
  31.                 final float t_inf = (float) index / NB_SAMPLES;  
  32.                 final float t_sup = (float) (index + 1) / NB_SAMPLES;  
  33.                 final float d_inf = SPLINE[index];  
  34.                 final float d_sup = SPLINE[index + 1];  
  35.                 final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);  
  36.                   
  37.                 mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));  
  38.                 // Pin to mMinX <= mCurrX <= mMaxX  
  39.                 mCurrX = Math.min(mCurrX, mMaxX);  
  40.                 mCurrX = Math.max(mCurrX, mMinX);  
  41.                   
  42.                 mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));  
  43.                 // Pin to mMinY <= mCurrY <= mMaxY  
  44.                 mCurrY = Math.min(mCurrY, mMaxY);  
  45.                 mCurrY = Math.max(mCurrY, mMinY);  
  46.   
  47.                 if (mCurrX == mFinalX && mCurrY == mFinalY) {  
  48.                     mFinished = true;  
  49.                 }  
  50.   
  51.                 break;  
  52.             }  
  53.         }  
  54.         else {  
  55.             mCurrX = mFinalX;  
  56.             mCurrY = mFinalY;  
  57.             mFinished = true;  
  58.         }  
  59.         return true;  
  60.     }  

从代码可以看到,如果我们没有设置插值器,就会调用内部默认算法。

[java] view plain copy
  1. /** 
  2.      * 函数翻译是粘性流体 
  3.      * 估计是一种算法 
  4.      */  
  5.     static float viscousFluid(float x)  
  6.     {  
  7.         x *= sViscousFluidScale;  
  8.         if (x < 1.0f) {  
  9.             x -= (1.0f - (float)Math.exp(-x));  
  10.         } else {  
  11.             float start = 0.36787944117f;   // 1/e == exp(-1)  
  12.             x = 1.0f - (float)Math.exp(1.0f - x);  
  13.             x = start + x * (1.0f - start);  
  14.         }  
  15.         x *= sViscousFluidNormalize;  
  16.         return x;  
  17.     }  
接着是两个重要的get方法

[java] view plain copy
  1. /** 
  2.      * Returns the current X offset in the scroll.  
  3.      *  
  4.      * @return The new X offset as an absolute distance from the origin. 
  5.      * 获得当前X方向偏移 
  6.      */  
  7.     public final int getCurrX() {  
  8.         return mCurrX;  
  9.     }  
  10.       
  11.     /** 
  12.      * Returns the current Y offset in the scroll.  
  13.      *  
  14.      * @return The new Y offset as an absolute distance from the origin. 
  15.      * 获得当前Y方向偏移 
  16.      */  
  17.     public final int getCurrY() {  
  18.         return mCurrY;  
  19.     }  

从scroller的整个源码,我们都可以看到,根本没有对控件进行重绘的操作。scroller只是一个利用滑动算法为控件提供新的绘制位置的工具,真正引起重绘的,是scrollTo方法,而这个方法需要我们主动调用(在computeScroll()里面)


另外scroller内部还有许多的get方法,还有强制停止动画效果的方法等,下面贴出scroller的完整代码和注释(我翻译得很烂,大家海涵),大家可以看看一下

[java] view plain copy
  1. /* 
  2.  * Copyright (C) 2006 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package android.widget;  
  18.   
  19. import android.content.Context;  
  20. import android.hardware.SensorManager;  
  21. import android.os.Build;  
  22. import android.util.FloatMath;  
  23. import android.view.ViewConfiguration;  
  24. import android.view.animation.AnimationUtils;  
  25. import android.view.animation.Interpolator;  
  26.   
  27.   
  28. /** 
  29.  * This class encapsulates scrolling.  The duration of the scroll 
  30.  * can be passed in the constructor and specifies the maximum time that 
  31.  * the scrolling animation should take.  Past this time, the scrolling is  
  32.  * automatically moved to its final stage and computeScrollOffset() 
  33.  * will always return false to indicate that scrolling is over. 
  34.  * 这个类封装了滑动。滑动时间可以通过构造函数传入,并且制定滑动动画完成所需的最大时间。 
  35.  * 这个时间以后,将会自动滑动到它的最终状态,并且computeScrollOffset()将返回false,表示滑动结束 
  36.  */  
  37. public class Scroller  {  
  38.     /** 
  39.      * 模式,有SCROLL_MODE和FLING_MODE 
  40.      */  
  41.     private int mMode;  
  42.     /** 
  43.      * 起始x方向偏移 
  44.      */  
  45.     private int mStartX;  
  46.     /** 
  47.      * 起始y方向偏移 
  48.      */  
  49.     private int mStartY;  
  50.     /** 
  51.      * 终点x方向偏移 
  52.      */  
  53.     private int mFinalX;  
  54.     /** 
  55.      * 终点y方向偏移 
  56.      */  
  57.     private int mFinalY;  
  58.   
  59.     private int mMinX;  
  60.     private int mMaxX;  
  61.     private int mMinY;  
  62.     private int mMaxY;  
  63.   
  64.     /** 
  65.      * 当前x方向偏移 
  66.      */  
  67.     private int mCurrX;  
  68.     /** 
  69.      * 当前y方向偏移 
  70.      */  
  71.     private int mCurrY;  
  72.     /** 
  73.      * 起始时间 
  74.      */  
  75.     private long mStartTime;  
  76.     /** 
  77.      * 滚动持续时间 
  78.      */  
  79.     private int mDuration;  
  80.     /** 
  81.      * 持续时间的倒数 
  82.      */  
  83.     private float mDurationReciprocal;  
  84.     /** 
  85.      * x方向应该滚动的距离,mDeltaX=mFinalX-mStartX 
  86.      */  
  87.     private float mDeltaX;  
  88.     /** 
  89.      * y方向应该滚动的距离,mDeltaY=mFinalY-mStartY 
  90.      */  
  91.     private float mDeltaY;  
  92.     /** 
  93.      * 是否结束 
  94.      */  
  95.     private boolean mFinished;  
  96.     /** 
  97.      * 插值器 
  98.      */  
  99.     private Interpolator mInterpolator;  
  100.     /** 
  101.      * 调速轮 
  102.      */  
  103.     private boolean mFlywheel;  
  104.   
  105.     private float mVelocity;  
  106.     /** 
  107.      * 默认滑动时间 
  108.      */  
  109.     private static final int DEFAULT_DURATION = 250;  
  110.     /** 
  111.      * 滑动模式 
  112.      */  
  113.     private static final int SCROLL_MODE = 0;  
  114.     /** 
  115.      * 猛冲模式 
  116.      */  
  117.     private static final int FLING_MODE = 1;  
  118.   
  119.     private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));  
  120.     private static float ALPHA = 800// pixels / seconds  
  121.     private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)  
  122.     private static float END_TENSION = 1.0f - START_TENSION;  
  123.     private static final int NB_SAMPLES = 100;  
  124.     private static final float[] SPLINE = new float[NB_SAMPLES + 1];  
  125.     /** 
  126.      * 减速率 
  127.      */  
  128.     private float mDeceleration;  
  129.     /** 
  130.      * pixels per inch 
  131.      */  
  132.     private final float mPpi;  
  133.   
  134.     static {  
  135.         float x_min = 0.0f;  
  136.         for (int i = 0; i <= NB_SAMPLES; i++) {  
  137.             final float t = (float) i / NB_SAMPLES;  
  138.             float x_max = 1.0f;  
  139.             float x, tx, coef;  
  140.             while (true) {  
  141.                 x = x_min + (x_max - x_min) / 2.0f;  
  142.                 coef = 3.0f * x * (1.0f - x);  
  143.                 tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;  
  144.                 if (Math.abs(tx - t) < 1E-5break;  
  145.                 if (tx > t) x_max = x;  
  146.                 else x_min = x;  
  147.             }  
  148.             final float d = coef + x * x * x;  
  149.             SPLINE[i] = d;  
  150.         }  
  151.         SPLINE[NB_SAMPLES] = 1.0f;  
  152.   
  153.         // This controls the viscous fluid effect (how much of it)  
  154.         sViscousFluidScale = 8.0f;  
  155.         // must be set to 1.0 (used in viscousFluid())  
  156.         sViscousFluidNormalize = 1.0f;  
  157.         sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);  
  158.     }  
  159.   
  160.     private static float sViscousFluidScale;  
  161.     private static float sViscousFluidNormalize;  
  162.   
  163.     /** 
  164.      * Create a Scroller with the default duration and interpolator. 
  165.      * 默认滑动时间和插值器 
  166.      */  
  167.     public Scroller(Context context) {  
  168.         this(context, null);  
  169.     }  
  170.   
  171.     /** 
  172.      * Create a Scroller with the specified interpolator. If the interpolator is 
  173.      * null, the default (viscous) interpolator will be used. "Flywheel" behavior will 
  174.      * be in effect for apps targeting Honeycomb or newer. 
  175.      * 调速轮只能在Honeycomb以上的版本有效 
  176.      */  
  177.     public Scroller(Context context, Interpolator interpolator) {  
  178.         this(context, interpolator,  
  179.                 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);  
  180.     }  
  181.   
  182.     /** 
  183.      * Create a Scroller with the specified interpolator. If the interpolator is 
  184.      * null, the default (viscous) interpolator will be used. Specify whether or 
  185.      * not to support progressive "flywheel" behavior in flinging. 
  186.      * 通过Ppi和滑动摩擦因素,计算减速率 
  187.      */  
  188.     public Scroller(Context context, Interpolator interpolator, boolean flywheel) {  
  189.         mFinished = true;  
  190.         mInterpolator = interpolator;  
  191.         mPpi = context.getResources().getDisplayMetrics().density * 160.0f;  
  192.         mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());  
  193.         mFlywheel = flywheel;  
  194.     }  
  195.   
  196.     /** 
  197.      * The amount of friction applied to flings. The default value 
  198.      * is {@link ViewConfiguration#getScrollFriction}. 
  199.      *  
  200.      * @param friction A scalar dimension-less value representing the coefficient of 
  201.      *         friction. 
  202.      * 设置fling方法的摩擦因素大小         
  203.      */  
  204.     public final void setFriction(float friction) {  
  205.         mDeceleration = computeDeceleration(friction);  
  206.     }  
  207.       
  208.     /** 
  209.      * 计算减速率 
  210.      */  
  211.     private float computeDeceleration(float friction) {  
  212.         return SensorManager.GRAVITY_EARTH   // g (m/s^2)  
  213.                       * 39.37f               // inch/meter  
  214.                       * mPpi                 // pixels per inch  
  215.                       * friction;  
  216.     }  
  217.   
  218.     /** 
  219.      *  
  220.      * Returns whether the scroller has finished scrolling. 
  221.      *  
  222.      * @return True if the scroller has finished scrolling, false otherwise. 
  223.      * 判断滑动是否停止 
  224.      */  
  225.     public final boolean isFinished() {  
  226.         return mFinished;  
  227.     }  
  228.       
  229.     /** 
  230.      * Force the finished field to a particular value. 
  231.      *   
  232.      * @param finished The new finished value. 
  233.      * 强制滑动停止 
  234.      */  
  235.     public final void forceFinished(boolean finished) {  
  236.         mFinished = finished;  
  237.     }  
  238.       
  239.     /** 
  240.      * Returns how long the scroll event will take, in milliseconds. 
  241.      *  
  242.      * @return The duration of the scroll in milliseconds. 
  243.      * 获得滑动时间 
  244.      */  
  245.     public final int getDuration() {  
  246.         return mDuration;  
  247.     }  
  248.       
  249.     /** 
  250.      * Returns the current X offset in the scroll.  
  251.      *  
  252.      * @return The new X offset as an absolute distance from the origin. 
  253.      * 获得当前X方向偏移 
  254.      */  
  255.     public final int getCurrX() {  
  256.         return mCurrX;  
  257.     }  
  258.       
  259.     /** 
  260.      * Returns the current Y offset in the scroll.  
  261.      *  
  262.      * @return The new Y offset as an absolute distance from the origin. 
  263.      * 获得当前Y方向偏移 
  264.      */  
  265.     public final int getCurrY() {  
  266.         return mCurrY;  
  267.     }  
  268.       
  269.     /** 
  270.      * Returns the current velocity. 
  271.      * 
  272.      * @return The original velocity less the deceleration. Result may be 
  273.      * negative. 
  274.      */  
  275.     public float getCurrVelocity() {  
  276.         return mVelocity - mDeceleration * timePassed() / 2000.0f;  
  277.     }  
  278.   
  279.     /** 
  280.      * Returns the start X offset in the scroll.  
  281.      *  
  282.      * @return The start X offset as an absolute distance from the origin. 
  283.      */  
  284.     public final int getStartX() {  
  285.         return mStartX;  
  286.     }  
  287.       
  288.     /** 
  289.      * Returns the start Y offset in the scroll.  
  290.      *  
  291.      * @return The start Y offset as an absolute distance from the origin. 
  292.      */  
  293.     public final int getStartY() {  
  294.         return mStartY;  
  295.     }  
  296.       
  297.     /** 
  298.      * Returns where the scroll will end. Valid only for "fling" scrolls. 
  299.      *  
  300.      * @return The final X offset as an absolute distance from the origin. 
  301.      */  
  302.     public final int getFinalX() {  
  303.         return mFinalX;  
  304.     }  
  305.       
  306.     /** 
  307.      * Returns where the scroll will end. Valid only for "fling" scrolls. 
  308.      *  
  309.      * @return The final Y offset as an absolute distance from the origin. 
  310.      */  
  311.     public final int getFinalY() {  
  312.         return mFinalY;  
  313.     }  
  314.   
  315.     /** 
  316.      * Call this when you want to know the new location.  If it returns true, 
  317.      * the animation is not yet finished.  loc will be altered to provide the 
  318.      * new location. 
  319.      * 调用这个函数获得新的位置坐标(滑动过程中)。如果它返回true,说明滑动没有结束。 
  320.      * getCurX(),getCurY()方法就可以获得计算后的值。 
  321.      */   
  322.     public boolean computeScrollOffset() {  
  323.         if (mFinished) {//是否结束  
  324.             return false;  
  325.         }  
  326.   
  327.         int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);//滑动开始,经过了多长时间  
  328.       
  329.         if (timePassed < mDuration) {//如果经过的时间小于动画完成所需时间  
  330.             switch (mMode) {  
  331.             case SCROLL_MODE:  
  332.                 float x = timePassed * mDurationReciprocal;  
  333.       
  334.                 if (mInterpolator == null)//如果没有设置插值器,利用默认算法  
  335.                     x = viscousFluid(x);   
  336.                 else//否则利用插值器定义的算法  
  337.                     x = mInterpolator.getInterpolation(x);  
  338.       
  339.                 mCurrX = mStartX + Math.round(x * mDeltaX);//计算当前X坐标  
  340.                 mCurrY = mStartY + Math.round(x * mDeltaY);//计算当前Y坐标  
  341.                 break;  
  342.             case FLING_MODE:  
  343.                 final float t = (float) timePassed / mDuration;  
  344.                 final int index = (int) (NB_SAMPLES * t);  
  345.                 final float t_inf = (float) index / NB_SAMPLES;  
  346.                 final float t_sup = (float) (index + 1) / NB_SAMPLES;  
  347.                 final float d_inf = SPLINE[index];  
  348.                 final float d_sup = SPLINE[index + 1];  
  349.                 final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);  
  350.                   
  351.                 mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));  
  352.                 // Pin to mMinX <= mCurrX <= mMaxX  
  353.                 mCurrX = Math.min(mCurrX, mMaxX);  
  354.                 mCurrX = Math.max(mCurrX, mMinX);  
  355.                   
  356.                 mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));  
  357.                 // Pin to mMinY <= mCurrY <= mMaxY  
  358.                 mCurrY = Math.min(mCurrY, mMaxY);  
  359.                 mCurrY = Math.max(mCurrY, mMinY);  
  360.   
  361.                 if (mCurrX == mFinalX && mCurrY == mFinalY) {  
  362.                     mFinished = true;  
  363.                 }  
  364.   
  365.                 break;  
  366.             }  
  367.         }  
  368.         else {  
  369.             mCurrX = mFinalX;  
  370.             mCurrY = mFinalY;  
  371.             mFinished = true;  
  372.         }  
  373.         return true;  
  374.     }  
  375.       
  376.     /** 
  377.      * Start scrolling by providing a starting point and the distance to travel. 
  378.      * The scroll will use the default value of 250 milliseconds for the 
  379.      * duration. 
  380.      *  
  381.      * @param startX Starting horizontal scroll offset in pixels. Positive 
  382.      *        numbers will scroll the content to the left. 
  383.      * @param startY Starting vertical scroll offset in pixels. Positive numbers 
  384.      *        will scroll the content up. 
  385.      * @param dx Horizontal distance to travel. Positive numbers will scroll the 
  386.      *        content to the left. 
  387.      * @param dy Vertical distance to travel. Positive numbers will scroll the 
  388.      *        content up. 
  389.      * 使用默认滑动时间完成滑动 
  390.      */  
  391.     public void startScroll(int startX, int startY, int dx, int dy) {  
  392.         startScroll(startX, startY, dx, dy, DEFAULT_DURATION);  
  393.     }  
  394.   
  395.     /** 
  396.      * Start scrolling by providing a starting point and the distance to travel. 
  397.      *  
  398.      * @param startX Starting horizontal scroll offset in pixels. Positive 
  399.      *        numbers will scroll the content to the left. 
  400.      *        滑动起始X坐标 
  401.      * @param startY Starting vertical scroll offset in pixels. Positive numbers 
  402.      *        will scroll the content up. 
  403.      *        滑动起始Y坐标 
  404.      * @param dx Horizontal distance to travel. Positive numbers will scroll the 
  405.      *        content to the left. 
  406.      *        X方向滑动距离 
  407.      * @param dy Vertical distance to travel. Positive numbers will scroll the 
  408.      *        content up. 
  409.      *        Y方向滑动距离 
  410.      * @param duration Duration of the scroll in milliseconds. 
  411.      *            完成滑动所需的时间      
  412.      * 从起始点开始滑动一定距离 
  413.      */  
  414.     public void startScroll(int startX, int startY, int dx, int dy, int duration) {  
  415.         mMode = SCROLL_MODE;  
  416.         mFinished = false;  
  417.         mDuration = duration;  
  418.         mStartTime = AnimationUtils.currentAnimationTimeMillis();//获取当前时间作为滑动的起始时间  
  419.         mStartX = startX;  
  420.         mStartY = startY;  
  421.         mFinalX = startX + dx;  
  422.         mFinalY = startY + dy;  
  423.         mDeltaX = dx;  
  424.         mDeltaY = dy;  
  425.         mDurationReciprocal = 1.0f / (float) mDuration;  
  426.     }  
  427.   
  428.     /** 
  429.      * Start scrolling based on a fling gesture. The distance travelled will 
  430.      * depend on the initial velocity of the fling. 
  431.      *  
  432.      * @param startX Starting point of the scroll (X) 
  433.      * @param startY Starting point of the scroll (Y) 
  434.      * @param velocityX Initial velocity of the fling (X) measured in pixels per 
  435.      *        second. 
  436.      * @param velocityY Initial velocity of the fling (Y) measured in pixels per 
  437.      *        second 
  438.      * @param minX Minimum X value. The scroller will not scroll past this 
  439.      *        point. 
  440.      * @param maxX Maximum X value. The scroller will not scroll past this 
  441.      *        point. 
  442.      * @param minY Minimum Y value. The scroller will not scroll past this 
  443.      *        point. 
  444.      * @param maxY Maximum Y value. The scroller will not scroll past this 
  445.      *        point. 
  446.      * 开始基于滑动手势的滑动。根据初始的滑动手势速度,决定滑动的距离(滑动的距离,不能大于设定的最大值,不能小于设定的最小值)        
  447.      */  
  448.     public void fling(int startX, int startY, int velocityX, int velocityY,  
  449.             int minX, int maxX, int minY, int maxY) {  
  450.         // Continue a scroll or fling in progress  
  451.         if (mFlywheel && !mFinished) {  
  452.             float oldVel = getCurrVelocity();  
  453.   
  454.             float dx = (float) (mFinalX - mStartX);  
  455.             float dy = (float) (mFinalY - mStartY);  
  456.             float hyp = FloatMath.sqrt(dx * dx + dy * dy);  
  457.   
  458.             float ndx = dx / hyp;  
  459.             float ndy = dy / hyp;  
  460.   
  461.             float oldVelocityX = ndx * oldVel;  
  462.             float oldVelocityY = ndy * oldVel;  
  463.             if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&  
  464.                     Math.signum(velocityY) == Math.signum(oldVelocityY)) {  
  465.                 velocityX += oldVelocityX;  
  466.                 velocityY += oldVelocityY;  
  467.             }  
  468.         }  
  469.   
  470.         mMode = FLING_MODE;  
  471.         mFinished = false;  
  472.   
  473.         float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);  
  474.        
  475.         mVelocity = velocity;  
  476.         final double l = Math.log(START_TENSION * velocity / ALPHA);  
  477.         mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));  
  478.         mStartTime = AnimationUtils.currentAnimationTimeMillis();  
  479.         mStartX = startX;  
  480.         mStartY = startY;  
  481.   
  482.         float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;  
  483.         float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;  
  484.   
  485.         int totalDistance =  
  486.                 (int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));  
  487.           
  488.         mMinX = minX;  
  489.         mMaxX = maxX;  
  490.         mMinY = minY;  
  491.         mMaxY = maxY;  
  492.   
  493.         mFinalX = startX + Math.round(totalDistance * coeffX);  
  494.         // Pin to mMinX <= mFinalX <= mMaxX  
  495.         mFinalX = Math.min(mFinalX, mMaxX);  
  496.         mFinalX = Math.max(mFinalX, mMinX);  
  497.           
  498.         mFinalY = startY + Math.round(totalDistance * coeffY);  
  499.         // Pin to mMinY <= mFinalY <= mMaxY  
  500.         mFinalY = Math.min(mFinalY, mMaxY);  
  501.         mFinalY = Math.max(mFinalY, mMinY);  
  502.     }  
  503.       
  504.     /** 
  505.      * 函数翻译是粘性流体 
  506.      * 估计是一种算法 
  507.      */  
  508.     static float viscousFluid(float x)  
  509.     {  
  510.         x *= sViscousFluidScale;  
  511.         if (x < 1.0f) {  
  512.             x -= (1.0f - (float)Math.exp(-x));  
  513.         } else {  
  514.             float start = 0.36787944117f;   // 1/e == exp(-1)  
  515.             x = 1.0f - (float)Math.exp(1.0f - x);  
  516.             x = start + x * (1.0f - start);  
  517.         }  
  518.         x *= sViscousFluidNormalize;  
  519.         return x;  
  520.     }  
  521.       
  522.     /** 
  523.      * Stops the animation. Contrary to {@link #forceFinished(boolean)}, 
  524.      * aborting the animating cause the scroller to move to the final x and y 
  525.      * position 
  526.      * 
  527.      * @see #forceFinished(boolean)      
  528.      * 强制停止滑动动画,并且将目的坐标设置为当前坐标 
  529.      */  
  530.     public void abortAnimation() {  
  531.         mCurrX = mFinalX;  
  532.         mCurrY = mFinalY;  
  533.         mFinished = true;  
  534.     }  
  535.       
  536.     /** 
  537.      * Extend the scroll animation. This allows a running animation to scroll 
  538.      * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}. 
  539.      * 
  540.      * @param extend Additional time to scroll in milliseconds. 
  541.      * @see #setFinalX(int) 
  542.      * @see #setFinalY(int) 
  543.      * 延长滚动时间 
  544.      */  
  545.     public void extendDuration(int extend) {  
  546.         int passed = timePassed();  
  547.         mDuration = passed + extend;  
  548.         mDurationReciprocal = 1.0f / mDuration;  
  549.         mFinished = false;  
  550.     }  
  551.   
  552.     /** 
  553.      * Returns the time elapsed since the beginning of the scrolling. 
  554.      * 
  555.      * @return The elapsed time in milliseconds. 
  556.      * 获得已经滚动的时间 
  557.      */  
  558.     public int timePassed() {  
  559.         return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);  
  560.     }  
  561.   
  562.     /** 
  563.      * Sets the final position (X) for this scroller. 
  564.      * 
  565.      * @param newX The new X offset as an absolute distance from the origin. 
  566.      * @see #extendDuration(int) 
  567.      * @see #setFinalY(int) 
  568.      * 设置mScroller最终停留的水平位置,没有动画效果,直接跳到目标位置 
  569.      */  
  570.     public void setFinalX(int newX) {  
  571.         mFinalX = newX;  
  572.         mDeltaX = mFinalX - mStartX;  
  573.         mFinished = false;  
  574.     }  
  575.   
  576.     /** 
  577.      * Sets the final position (Y) for this scroller. 
  578.      * 
  579.      * @param newY The new Y offset as an absolute distance from the origin. 
  580.      * @see #extendDuration(int) 
  581.      * @see #setFinalX(int) 
  582.      * 设置mScroller最终停留的竖直位置,没有动画效果,直接跳到目标位置 
  583.      */  
  584.     public void setFinalY(int newY) {  
  585.         mFinalY = newY;  
  586.         mDeltaY = mFinalY - mStartY;  
  587.         mFinished = false;  
  588.     }  
  589.   
  590.     /** 
  591.      * @hide 
  592.      */  
  593.     public boolean isScrollingInDirection(float xvel, float yvel) {  
  594.         return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) &&  
  595.                 Math.signum(yvel) == Math.signum(mFinalY - mStartY);  
  596.     }  
  597. }  


更重要的是创建scroller来解决问题的这种思想,有点像记账簿模式(记不清了,但肯定是设计模式的一种体现)

0 0
原创粉丝点击