• Android scroller的原理分析

    发布:51Code 时间: 2016-12-20 14:25

  • 谷歌为什么要设计一个scroller? 在Android中所有的的View都有一个实际界面大于可视界面的,这就涉及到界面的移动或者说偏移,View这个类提供了scrollTo和ScrollBy方法来实现界面的滚动,但...

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

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

    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一些方法罢了,并不能帮助我们滚动。

    如何实现滚动?

    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()会再一次刷新,这样会形成一个不间断刷新过程

            }

    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)

  • 上一篇:浅谈Android的回调

    下一篇:Android横竖屏切换时的生命周期

网站导航
Copyright(C)51Code软件开发网 2003-2021 , 沪ICP备05003035号-6