Scroller真的能滚吗?

View的scrollTo或者scrollBy方法可以将View内容滚动到指定位置,但是两者都是瞬时完成的,没有过渡动作。为了提升用户体验,有时我们希望可以实现view平滑的滚动,这时Scroller就派上用场了。

其实Scroller本身并不能滚动View,通常需要跟View的computeScroll配合使用才能发挥作用。例如我们自定义Layout:

public class ScrollerTestLayout extends FrameLayout {
    private Scroller mScroller;
    public ScrollerTestLayout(Context context) {
        this(context, null);
    }
    public ScrollerTestLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
    }
    public void scroll(int delta){
        mScroller.startScroll(getScrollX(), 0, delta, 0);
        postInvalidate();
    }
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            this.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }
}

当调用srcoll方法时,layout里的内容将在水平方向上整体向左移动delta大小的距离(如果delta<0,将向右移动)。那Scroller到底是怎样工作的呢?首先看下startScroll()方法源码。

public void startScroll(int startX, int startY, int dx, int dy) {
      startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
/**
* Start scrolling by providing a starting point, the distance to travel, 
* and the duration of the scroll.
*/
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;
}

可以看到,Scroller的startScroll方法其实什么也没做,只是记录一些需要的数据。真正的触发应该来源于postInvalidate()或者invalidate()方法,这两个方法会导致view重绘,重绘过程中会调用computeScroll()方法。在该方法中我们手动调用scrollTo滚动view内容到Scroller为我们指定的位置,即getCurrX()和getCurrY()。至于这个具体位置,Scorller的computeScrollOffset()会自动为我们计算,并返回boolean值告知我们滑动是否结束。如果滑动没有结束,我们继续调用postInvalidate()方法重绘,这样就形成一个循环直至Scroller计算结束。

/**
 * 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:
                // fling
                ...
    } else {
        mCurrX = mFinalX;
        mCurrY = mFinalY;
        mFinished = true;
    }
    return true;
}

从源码可以看出,Scroller会根据插值器计算时间流逝的占比,最终计算出view内容的位置信息。当然,我们也可以自定义插值器,按照我们需要的规则平滑滚动view内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值