1.谷歌为什么要设计一个scroller?

在android中所有的的View都有一个实际界面大于可视界面的,这就涉及到界面的移动或者说偏移,View这个类提供了scrollTo和ScrollBy方法来实现界面的滚动,但是这两种滚动都是即刻瞬间的,对于用户来说是不友好的,这个时候就需要一个滚动器来拉长这个滚动过程。也就是我们的Scroller,这个滚动器的构造方法需要一个durration来设置滚动时间。

2.Scroller的作用的是什么?

实际上Scroller并不负责界面的实际滚动,虽然这个名字让人误解,但实际上它就是一个工具类,它有两个最重要的方法,一个是startScroll(),这个方法就是根据scroller的构造方法中的x,y,distance,durration这些个参数来初始化一些参数,看源码,和我的注释,不必多言:

方法一:

/**
      * Start scrolling by providing a starting point and the distance to travel.
      * 
      * @param startX Starting horizontal scroll offset in pixels. Positive
      *        numbers will scroll the content to the left.
      * @param startY Starting vertical scroll offset in pixels. Positive numbers
      *        will scroll the content up.
      * @param dx Horizontal distance to travel. Positive numbers will scroll the
      *        content to the left.
      * @param dy Vertical distance to travel. Positive numbers will scroll the
      *        content up.
      * @param duration Duration of the scroll in milliseconds.
      */
     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; //重点横坐标=起点坐标与distance的和
         mFinalX = startX + dx;
         mFinalY = startY + dy;
         mDeltaX = dx;
         mDeltaY = dy; //持续时间的一个倒数,后面更新滚动后的坐标
         mDurationReciprocal = 1.0f / (float) mDuration;
     }

方法二:

/**
      * Call this when you want to know the new location.  If it returns true,
      * the animation is not yet finished.  loc will be altered to provide the
      * new location.
      */ 
     public boolean computeScrollOffset() {
         if (mFinished) {
             return false;
         }


         int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
     
         if (timePassed < mDuration) {
             switch (mMode) {
             case SCROLL_MODE:
                 float x = 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:
                 final float t = (float) timePassed / mDuration;
                 final int index = (int) (NB_SAMPLES * t);
                 final float t_inf = (float) index / NB_SAMPLES;
                 final float t_sup = (float) (index + 1) / NB_SAMPLES;
                 final float d_inf = SPLINE[index];
                 final float d_sup = SPLINE[index + 1];
                 final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);
                 
                 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;
     }

这个方法无需多言,也是更新mCurrY 等状态值。


综上所述,scroller这个类就是一个工具类,get,set一些方法罢了,并不能帮助我们滚动。



3.如何实现滚动?


android View类的scrollTo()方法结合Scroller一起使用。具体就是View初始化的时候startScroll,然后去实现每一个ViewGroup中都要实现的一个computeScroll方法,这个方法才是真正控制滚动的方法。我们只需要在我们的ViewGroup中调用这两个方法即可。具体就是copumteScroll中

if (mScroller.computeScrollOffset()) {//为true的时候说明没有滚动到终点
             if (getScrollX() != mScroller.getCurrX()) {
                 scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
             }
             invalidate();//scrollTo会导致界面刷新,invalidate()会再一次刷新,这样会形成一个不间断刷新过程
         }


4.View调用computeScroll的原理及过程?

View draw(Canvas can)有6个过程

1.绘制bg  2.保存一些Canvas的layer的状态,为横竖的fadding edge绘制做准备 3.绘制content,也就是调用OnDraw方法来调用View具体的绘图实现。4.绘制childView,这个是针对ViewGroup的,在View中这个是一个空方法,在ViewGroup中的这个方法中会调用drawChild这个方法,这个方法就会调用computeScroll方法,实现滚动。(5.恢复canvas 的layer状态,绘制fadding edge,6.绘制scrollBar)