Android-Scroller使用以及原理

简介

使用scollTo/scollBy方法进行滑动时,这个过程是瞬间完成的,所以用户体验不大好。如果使用 Scroller 来实现有过渡效果的滑动,这个过程不是瞬间完成的,而是在一定的时间间隔内完成的。 Scroller本身是不能实现View的滑动的,它需要与View的computeScroll()方法配合才能实现弹性滑动的效果。

使用

在自定义View的构造函数中初始化Scroller。

public CustomView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mScroller = new Scroller(context);
}

重写computeScroll方法,系统会在绘制View的时候在draw()方法中调用该方法。在这个 方法中,我们调用父类的scrollTo()方法并通过Scroller来不断获取当前的滚动值,每滑动一小段距离我们 就调用invalidate()方法不断地进行重绘,重绘就会调用computeScroll()方法,这样我们通过不断地移动 一个小的距离并连贯起来就实现了平滑移动的效果。

@Override
public void computeScroll() {
    super.computeScroll();
    if (mScroller.computeScrollOffset()) {
        ((View) getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
        invalidate();
    }
}

在 CustomView 中写一个 smoothScrollTo 方法,调用 Scroller 的 startScroll()方法,在2000ms内 沿X轴平移delta像素。

public void smoothScrollTo(int x, int y) {
   int scrollX = getScrollX();
   int delta = x - scrollX;
   mScroller.startScroll(scrollX, 0, delta, 0, 2000);
   invalidate();
}

通过以下代码触发平移动作。

mCustomView.smoothScrollTo(-400,0);

效果:
在这里插入图片描述

原理

我们通过调用smoothScrollTo来开始活动,在smoothScrollTo中在调用了Scroller对象的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; //滑动开始的X位置
    mStartY = startY; //滑动开始的Y位置
    mFinalX = startX + dx; //滑动结束的X位置
    mFinalY = startY + dy; //滑动结束的Y位置
    mDeltaX = dx; //X偏移量
    mDeltaY = dy; //Y偏移量
    mDurationReciprocal = 1.0f / (float) mDuration; //滑动持续时间的倒数
}

可见startScroll只是给一些变量赋值,并没有进行实际的操作。在我们调用invalidate方法后,View会进行重绘,重绘时会调用computeScroll方法。在computeScroll中,我们调用了computeScrollOffset方法,该方法源码如下。

public boolean computeScrollOffset() {
if (mFinished) { //如果滑动已经结束,返回false。
    return false;
}

int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); //滑动动画已经进行的时长

if (timePassed < mDuration) { //滑动动画已经进行的时长是否超过规定的时长(mDuration)
    switch (mMode) { //判断滑动的模式
    case SCROLL_MODE:
        final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
        /*
		计算出当前应该处于的X位置
        计算出当前应该处于的Y位置
        */
        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;
}

computeScroll方法:

@Override
public void computeScroll() {
    super.computeScroll();
    if (mScroller.computeScrollOffset()) { //判断滑动是否结束
        ((View) getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //移动View
        invalidate(); //重绘View
    }
}

总结

Scroller并不能直接实现View的滑动,它需要配合View的computeScroll()方法。在computeScroll()中不断让View进 行重绘,每次重绘都会计算滑动持续的时间,根据这个持续时间就能算出这次View滑动的位置,我们根据 每次滑动的位置调用scrollTo()方法进行滑动,这样不断地重复上述过程就形成了弹性滑动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值