Android进阶之路读书笔记-Scroller解析

(此文接续 view滑动的方法 一文)
Scroller的构造方法:

 public Scroller(Context context) {
        this(context, null);
    }

    /**
     * Create a Scroller with the specified interpolator. If the interpolator is
     * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
     * be in effect for apps targeting Honeycomb or newer.
     */
    public Scroller(Context context, Interpolator interpolator) {
        this(context, interpolator,
                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
    }

    /**
     * Create a Scroller with the specified interpolator. If the interpolator is
     * null, the default (viscous) interpolator will be used. Specify whether or
     * not to support progressive "flywheel" behavior in flinging.
     */
    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这个插值器.然后是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;
    }

这里面主要是保存传进来的参数,没有开启滑动的方法.startX和startY表示滑动开始的起点,dx和dy表
示滑动的距离,duration则表示滑动持续的时间.使view滑动主要是我们调用了invalidate()方法

public void smoothTo(int x1,int y1) {
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        int disX = x1 - scrollX;
        int disY = y1 - scrollY;
        scroller.startScroll(scrollX,scrollY,disX,disY,2000);
        invalidate();
    }

invalidate()方法会是view重新绘制,而View的重绘会调用View的draw()方法,draw()方法又会调用View的computeScroll()方法,即会调用我们重写的computeScroll()方法

   /**
     * 此方法会在onDraw()方法中调用
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (scroller.computeScrollOffset()){
            ((View)getParent()).scrollTo(scroller.getCurrX(),scroller.getCurrY());
            invalidate();
        }
    }

接下来我们看下computeScrollOffset()方法(此方法返回true表示滑动未结束,false表示滑动结束)

  /**
     * 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;
    }

首先计算了timePassed,也就是动画的持续时长,如果timePassed< mDuration(也就是说当动画持续时长小于我们设置的时长的时候),回去执行switch语句.在startScroll()方法中的mMode值为SCROLL_MODE,所以执行分支语句 SCROLL_MODE,然后根据插值器 Interpolator 来计算出在该时间段内移动的距离,赋值给mCurrX和mCurrY,这样我们就能通过Scroller的getCurrX()和getCurrY()方法来获取当前的ScrollX和ScrollY了
总结一下Scroller的原理
Scroller并不能直接实现View的滑动,它需要配合View的computeScroll()方法。在computeScroll()中不断让View进行重绘,每次重绘都会计算滑动持续的时间,根据这个持续时间就能算出这次View滑动的位置,我们根据每次滑动的位置调用scrollTo()方法进行滑动,这样不断地重复上述过程就形成了弹性滑动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值