一个滑动组件的原理浅析

前言

一个好的滑动不止能够响应手指的移动,而且还能响应 Fling(抛) 事件,响应手指的移动比较简单,手指移动多少距离,布局就移动多少距离;当快速滑动时,手指离开后,不能马上停止滑动,而是应该计算手指的移动速度,产生一个【抛】(Fling )的动作,让内容继续滚动一段距离。这才是好的滑动组件。那么其中具体的设计究竟是如何呢,今天就来分析一波,借鉴 Android OverScroller 的实现。

Demo

if (mOverScroller.computeScrollOffset()) {
      // 获取 scrollY
     int y = mOverScroller.getCurrY();
     // 此处省略一堆控制滚动的代码
     .....
     // 更新
    postInvalidate();
}

解析

总体逻辑先梳理下,首先内容跟随手指移动而【移动】,手指离开时产生一个 【速度】,根据这个速度计算一个,此次滑动的【总时间】和【总距离】,之后可以计算每一个【时刻】对应的【位置】。那其中时刻与距离的对应关系是如何呢?看了一下 Android 的源码,计算关系之复杂,惨绝人寰,欲研又止!但这不重要,重要的是掌握全局。
一起来欣赏一下代码吧

/*
         * Update the current position and velocity for current time. Returns
         * true if update has been done and false if animation duration has been
         * reached.
         */
        boolean update() {
            final long time = AnimationUtils.currentAnimationTimeMillis();
            final long currentTime = time - mStartTime;

            if (currentTime == 0) {
                // Skip work but report that we're still going if we have a nonzero duration.
                return mDuration > 0;
            }
            if (currentTime > mDuration) {
                return false;
            }

            double distance = 0.0;
            switch (mState) {
                case SPLINE: {
                    final float t = (float) currentTime / mSplineDuration;
                    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;
                    }

                    distance = distanceCoef * mSplineDistance;
                    mCurrVelocity = velocityCoef * mSplineDistance / mSplineDuration * 1000.0f;
                    break;
                }

                case BALLISTIC: {
                    final float t = currentTime / 1000.0f;
                    mCurrVelocity = mVelocity + mDeceleration * t;
                    distance = mVelocity * t + mDeceleration * t * t / 2.0f;
                    break;
                }
                .....
            }

            mCurrentPosition = mStart + (int) Math.round(distance);

            return true;
        }

mCurrentPosition 就是某个时刻对应的位置,有趣的是 BALLISTIC 模式,还记得高中物理的重力加速度时间与距离的公式吗?是不是似曾相识?这算比较简单的公式,但我们今天但主角是 SPLINE 模式,ScrollView 但滑动正是使用了这个模式进行滑动,体验非常好!!!!公式有点复杂,就不洗洗研究了,喜欢的同学自行研究吧

总结

简单一个滚动,蕴藏的道理生不可测,一不小心就出现了物理知识点......学习还是挺有用的吧

你可能感兴趣的:(一个滑动组件的原理浅析)