Scroller 均匀滑动

  • 用途
    实现有过渡效果的滑动
  • 使用
Scroller scroller = new Scroller(context);

private void smoothScrollTo(int destX, int destY){
    int scrollX = getScrollX();
    int delta = destX - scrollX;
    scroller.startScroll(scrollX, 0, delta, 0, 1000);
    invalidate();
}

@Override
public void computeScroll(){
    if (scroller.computeScrollOffset()){
        scrollTo(scroller.getCurrX(),scroller.getCurrY());
        postInvalidate();
    }
}
  • 解析
  1. 构造函数
   /**
     * 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
    }
  1. startScroll: 初始化相关参数
/**
    * Start scrolling by providing a starting point, the distance to travel,
    * and the duration of the scroll.
    * 
    * @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;
       mFinalX = startX + dx;
       mFinalY = startY + dy;
       mDeltaX = dx;
       mDeltaY = dy;
       mDurationReciprocal = 1.0f / (float) mDuration;
   }

设置了一些初始变量

  1. computeScrollOffset 关键方法,计算此时的位置,当结束则返回false,否则返回true
   /**
     * 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;
    }
  • 重复过程
    invalidate() --->invoke view.draw() ---> invoke computeScroll()
    --->postInvalidate
  • 总结
    Scroller本身并不能实现View的滑动,需要配合View的computeScroll方法才能完成弹性滑动的效果。它不断的让View重绘,而每一次重绘距离滑动起始时间会有一个时间间隔,通过这个时间间隔Scroller就可以得到View当前的滑动位置,从而可以通过scrollTo方法来完成View的滑动。因此,通过每一次重绘导致View进行小幅度的滑动,而多次小幅度滑动就组成了弹性滑动。

你可能感兴趣的:(Scroller 均匀滑动)