美文网首页
3.3 弹性滑动

3.3 弹性滑动

作者: 武安长空 | 来源:发表于2016-06-20 14:47 被阅读39次

1. Scroller原理分析

经典使用

Scroller scroller = new Scroller(getContext());

// 缓慢滚动到指定位置
private void smoothScrollTo(int destX, int destY) {
    int startX = (int) getX();
    int deltaX = startX - destX;
    int startY = (int) getY();
    int deltaY = startY - destY;
    // 2000ms内滑向目标位置,效果就是慢慢滑动
    // dx,dy的计算是:起始坐标-目标坐标,否则会反向跑
    scroller.startScroll(startX, startY, deltaX, deltaY, 2000);
    invalidate();
}

@Override
public void computeScroll() {
    super.computeScroll();
    if (scroller.computeScrollOffset()) {
        scrollTo(scroller.getCurrX(), scroller.getCurrY());
        postInvalidate();
    }
}

首先看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;
    mStartY = startY;
    mFinalX = startX + dx;
    mFinalY = startY + dy;
    mDeltaX = dx;
    mDeltaY = dy;
    mDurationReciprocal = 1.0f / (float) mDuration;
}

这个方法只是保存了一些值,没有对View有操作。真正让View弹性滑动的是invalidate方法。invalidate方法会导致View重绘,在viwe的draw方法中会调用computeScroll方法,这个方法在View中是空实现,所以需要我们重写

/**
 * Called by a parent to request that a child update its values for mScrollX
 * and mScrollY if necessary. This will typically be done if the child is
 * animating a scroll using a {@link android.widget.Scroller Scroller}
 * object.
 */
public void computeScroll() {
}

在我们重写的方法中,我们首先调用scroller的computeScrollOffset方法去判断是否继续滚动到新位置,返回true则我们执行scrollTo方法,使View的内容滚动,并继续调用postInvalide方法。直到整个滑动过程结束。

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

2. 通过动画模拟Scroller

final int startX = 0;
final int deltaX = 200;
ValueAnimator animator = ObjectAnimator.ofInt(0, 1).setDuration(2000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float fraction = animation.getAnimatedFraction();
        // 内容向左滚动是正的
        view.scrollTo(startX + (int) (deltaX * fraction), 0);
    }
});
animator.start();

我们动画本质上没有作用于任何对象,它只是在2秒内完成动画过程,利用这个特性,我们在动画每一帧到来时获取动画的完成比例,再根据比例计算当前View要滚动的距离,再通过Vieww本身的scrollTo方法完成滚动。这个过程和Scroller的过程很相似。

3. 通过延时策略完成弹性滚动

思路和上面的很相似。以handler为例,如果没有完成滚动距离,就不断发消息让view滚动,完成滚动距离则停止发送。代码如下:

private final int MESSAGE_SCROLL_TO = 1;
private int frame_count = 30;
private int count = 0;
Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
            case MESSAGE_SCROLL_TO:
                count++;
                if (count<=frame_count){
                    float fraction = count/(float)frame_count;
                    int scrollX = (int) (fraction*200);
                    view.scrollTo(scrollX,0);
                    handler.sendEmptyMessage(MESSAGE_SCROLL_TO);
                }
                break;
        }
    }
};

相关文章

  • 3.3 弹性滑动

    1. Scroller原理分析 经典使用 首先看Scroller.startScroll方法 这个方法只是保存了一...

  • 3.3 弹性滑动

    弹性滑动实现方式有很多,但它们都有一个共同思想:将一次大的滑动分成若干次小的滑动并在一段时间内完成实现方式有很多,...

  • 弹性滑动

    3.3 弹性滑动 知道了View的滑动,我们还要知道如何实现View的弹性滑动,比较生硬的滑动过去,这种方式的用户...

  • view滑动

    View的滑动 Scroller 弹性滑动对象,用来实现View的弹性滑动。首先看scrollto跟scrollb...

  • 弹性滑动典型代码

    弹性滑动对象,用于实现View的弹性滑动。当使用View的scrollTo/scrollBy方法进行滑动时,其过程...

  • Android View的事件体系(三) 弹性滑动

    弹性滑动 知道了View的滑动,我们还要知道如何实现View的弹性滑动,比较生硬的滑动过去,这种方式的用户体验实在...

  • Android开发之Scroller

    什么是Scroller? 翻译为弹性滑动对象,可以实现View的弹性滑动动画,与Scroller相关的就是大家比较...

  • Scroller(触摸滑屏)

    什么是Scroller?翻译为弹性滑动对象,可以实现View的弹性滑动动画,与Scroller相关的就是大家比较熟...

  • Android View的事件体系(三)弹性滑动

    比较生硬地滑动一个View,用户体验实在是太差了,所以我们需要实现优雅的弹性滑动效果。那么如何实现弹性滑动呢? 主...

  • 1源码的角度分析View

    内容:view基础、view滑动、弹性滑动、横纵滑动冲突 view基础 获取view的宽高:width = rig...

网友评论

      本文标题:3.3 弹性滑动

      本文链接:https://www.haomeiwen.com/subject/yvpwdttx.html