改变ViewPager的滑动时间 以及 Scroller浅析

改变ViewPager的滑动时间 以及 Scroller浅析

  有一个滑动效果,产品提出了两点需求:

  • 滑动时要先加速,后减速
  • 滑动的时间需要比旧版长一点

  这个滑动效果之前是使用ViewPager来实现的,一时不想做太大改变,但苦于ViewPager没有暴露改变滑动时长的接口,于是查了一下,看到StackOverflow上有位大侠提供了一种方法:使用反射,将控制ViewPager的Scroller实例化,然后用我们自己的Scroller来set进去。

try {
    Field mScroller;
    mScroller = ViewPager.class.getDeclaredField("mScroller");
    mScroller.setAccessible(true); 
    FixedSpeedScroller scroller = new FixedSpeedScroller(mPager.getContext(), sInterpolator);
    // scroller.setFixedDuration(5000);
    mScroller.set(mPager, scroller);
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}

  这样就可以改变ViewPager的滑动时间了,同时也可以实现“先加速后减速”的效果。

  说到这里,顺便说一下Scroller吧~其实在之前的实现双向滑动SlideMenu中,已经粗略地讲到了Scroller使用方法,现在就从源码中一起探究一下吧~

  在探究Scroller之前,我们最好从远处,也就是宏观的角度,想一想“为什么会有Scroller这个类?Scroller这个类是干什么的?”要回答这个问题,最好的方法是:看文档(废话-_-)

This class encapsulates scrolling. You can use scrollers (Scroller or OverScroller) to collect the data you need to produce a scrolling animation—for example, in response to a fling gesture. Scrollers track scroll offsets for you over time, but they don’t automatically apply those positions to your view. It’s your responsibility to get and apply new coordinates at a rate that will make the scrolling animation look smooth.

  简而言之:Scroller就是帮助View在改变画布坐标时无比丝滑的~

  大家都知道,在Android中View的canvas实际上是无限大的,而我们平时之所以看到的是canvas的一部分,是因为一个View的绘制受到了它父视图的限制。换句话说,一个View由于受父视图的限制,只能显示它无限大的canvas的一部分。那么问题就来了:canvas无限大,应该显示它的哪一部分呢?

  鉴于这个问题,View就提供了两个变量mScrollX和mScrollY。这两个变量分别标识视图内容相对于坐标原点位置的x方向的偏移和y方向的偏移。同时,View也提供了两个方法scrollTo()和scrollBy()来对视图进行平移。不过使用这两个方法时,视图会瞬间偏移到指定地方,这对用户而言肯定不友好啊。。这就像一秒钟把用户从中国位移到美国,那个人精神不错乱才怪-_-

  为了用户的体验起鉴,Scroller就应运而生了~

  使用Scroller时有两个主要方法:startScroll()和computeScrollOffset()

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

  可以看到,startScroll()方法就是给scroll动作相关变量赋初值,computeScrollOffset()方法主要就是计算此时View滑动的位置mCurrX和mCurrY。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值