浅探OverScroller

来源:互联网 发布:java工程师发展前景 编辑:程序博客网 时间:2024/04/30 01:14

最近有点儿纠结listview是怎么实现滑翔运行的(也就是抛出之后,自行滑动一段时间),一开始我以为用到了什么高大上的算法,于是想从源码中查找,结果没发现,不过反而让我发现了一点点新东西。

if (mFlingRunnable == null) {                mFlingRunnable = new FlingRunnable();            }
然后我就对FlingRunnable这个类产生了点兴趣,看看是这样的。

FlingRunnable() {            mScroller = new OverScroller(getContext());        }
一看是用到了OverScroller这个类,之后有某处会调用这个方法

void start(int initialVelocity) {            // 省略            mScroller.fling(0, initialY, 0, initialVelocity,                    0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);            // 省略        }
因此,我就猜测,类似于滑翔运动是基础OverScroller并且与fling这个方法有关,

对于这个方法,是这样介绍的

/**     * Start scrolling based on a fling gesture. The distance traveled will     * depend on the initial velocity of the fling.     *     * @param startX Starting point of the scroll (X)     * @param startY Starting point of the scroll (Y)     * @param velocityX Initial velocity of the fling (X) measured in pixels per     *            second.     * @param velocityY Initial velocity of the fling (Y) measured in pixels per     *            second     * @param minX Minimum X value. The scroller will not scroll past this point     *            unless overX > 0. If overfling is allowed, it will use minX as     *            a springback boundary.     * @param maxX Maximum X value. The scroller will not scroll past this point     *            unless overX > 0. If overfling is allowed, it will use maxX as     *            a springback boundary.     * @param minY Minimum Y value. The scroller will not scroll past this point     *            unless overY > 0. If overfling is allowed, it will use minY as     *            a springback boundary.     * @param maxY Maximum Y value. The scroller will not scroll past this point     *            unless overY > 0. If overfling is allowed, it will use maxY as     *            a springback boundary.
因为是listview,不存在x方向上的变化,因此跟x有关的参数都变成0了

一般来说,因为速度上是有方向的,但在这里可以看到

     * @param minY Minimum Y value. The scroller will not scroll past this point     *            unless overY > 0. If overfling is allowed, it will use minY as     *            a springback boundary.

意思是,滚轴不会滚过这一点,除非overY>0,如果允许overfling,它将会使用minY作为回弹边界。

这在这里,一般情况下,minY都会被设置为0,也就是说,速度方向为负数的情况,只能得到为0的滑动距离。

至于解决方案,在下方讲解会说明。

为了测试滑翔运动是不是用这个类来解决,本博主模仿了一下,并且得到一些数据。


本例子也是基于listview实现的,是为了更加准确的验证滑动距离,还有数据的变化会不会随着listview的停止而停止。

初始的时候,需要有一个手势来获取抛出的速度

private GestureDetector mGestureDetector;
并且要在构造器实现
mGestureDetector = new GestureDetector(context,new MyOnGestureListener());


这个MyOnGestureListener也是比较简洁,只实现了一个onFling方法,也就是为了获取velocityY,再次说明velocityY是有正负号的。
class MyOnGestureListener extends GestureDetector.SimpleOnGestureListener{        @Override        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {                        return false;        }    }

手势要能生效,这个步骤是必不可缺的。

    @Override    public boolean onTouchEvent(MotionEvent ev) {        mGestureDetector.onTouchEvent(ev);        return super.onTouchEvent(ev);    }

之后写了一个类,实现Runnable

private class FlingScroller implements Runnable{        OverScroller mFlingScroller;        boolean isRunning;        public FlingScroller(){            mFlingScroller = new OverScroller(getContext());        }


post(this)是view的一个方法,可以接收Runnable参数。
<span style="white-space:pre"></span>private void postExecute() {            if (isRunning)                post(this);        }        private void start(){            isRunning = true;            postExecute();        }


        void withFling( int velocityY) {            int initialY = velocityY < 0 ? Integer.MAX_VALUE : 0;            mFlingScroller.fling(0,initialY,0,velocityY,0, Integer.MAX_VALUE,0,Integer.MAX_VALUE);        }
在这个方法,用来传入一开始的y方向上的速度

如果速度<0,让他的初始值为int的最大值,否则为0


上面调用post(this)后,会执行:

        @Override        public void run() {            boolean endAnima = true;            if (mFlingScroller.computeScrollOffset()){                final int startY = mFlingScroller.getStartY();                int currY = startY==Integer.MAX_VALUE ?Integer.MAX_VALUE-mFlingScroller.getCurrY():mFlingScroller.getCurrY();                Log.d("FlingScroller", "currY:" + currY);                endAnima = false;            }            if (!endAnima){                postExecute();            }else {                isRunning = false;            }        }

分析这个:

if (mFlingScroller.computeScrollOffset()){                final int startY = mFlingScroller.getStartY();                int currY = startY==Integer.MAX_VALUE ?Integer.MAX_VALUE-mFlingScroller.getCurrY():mFlingScroller.getCurrY();                Log.d("FlingScroller", "currY:" + currY);                endAnima = false;            }

startY是获取一开始的initialY值,currY也是根据startY来计算的,因为手势向上滑动时(屏幕向下滚动),初速度为负,当前位置也不断变小,因此用Integer的最大值减去当前位置,可以获取正确的当前位置。


根据这段做一下测试,用最大速度抛出滑动

            if (mFlingScroller.computeScrollOffset()){                final int startY = mFlingScroller.getStartY();                int currY = startY==Integer.MAX_VALUE ?Integer.MAX_VALUE-mFlingScroller.getCurrY():mFlingScroller.getCurrY();                Log.d("FlingScroller", "currY:" + currY);                endAnima = false;            }
开头一部分:

10-03 01:24:23.654 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:16010-03 01:24:23.674 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:48010-03 01:24:23.698 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:84710-03 01:24:23.722 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:124310-03 01:24:23.750 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:163710-03 01:24:23.774 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:204210-03 01:24:23.802 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:2441


中间一部分:

10-03 01:24:24.374 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:914910-03 01:24:24.402 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:935610-03 01:24:24.422 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:954110-03 01:24:24.454 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:974310-03 01:24:24.526 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1026010-03 01:24:24.550 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:10417


结尾一部分:

10-03 01:24:26.054 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1435010-03 01:24:26.078 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1435610-03 01:24:26.102 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1436210-03 01:24:26.126 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1436610-03 01:24:26.154 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1436910-03 01:24:26.178 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1437110-03 01:24:26.202 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1437210-03 01:24:26.226 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:14372

得出结论,一开始速度一直增加,中间时候速度匀速,结尾时速度开始减小至0.


OverScroller还有一个比较重要的方法。


    /**     * The amount of friction applied to flings. The default value     * is {@link ViewConfiguration#getScrollFriction}.     *     * @param friction A scalar dimension-less value representing the coefficient of     *         friction.     */    public final void setFriction(float friction) {        mScrollerX.setFriction(friction);        mScrollerY.setFriction(friction);    }

这个参数是用来设置摩擦系数的,根据api的说明,默认值为ViewConfiguration.getScrolFriction。即:

    public static float getScrollFriction() {        return SCROLL_FRICTION;    }


    /**     * The coefficient of friction applied to flings/scrolls.     */    private static final float SCROLL_FRICTION = 0.015f;

默认值为0.015f。


源码下载:https://github.com/q742972035/measurelistviewofsliding




1 0
原创粉丝点击