浅探OverScroller

最近有点儿纠结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:160
10-03 01:24:23.674 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:480
10-03 01:24:23.698 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:847
10-03 01:24:23.722 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1243
10-03 01:24:23.750 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:1637
10-03 01:24:23.774 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:2042
10-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:9149
10-03 01:24:24.402 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:9356
10-03 01:24:24.422 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:9541
10-03 01:24:24.454 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:9743
10-03 01:24:24.526 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:10260
10-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:14350
10-03 01:24:26.078 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:14356
10-03 01:24:26.102 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:14362
10-03 01:24:26.126 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:14366
10-03 01:24:26.154 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:14369
10-03 01:24:26.178 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:14371
10-03 01:24:26.202 18887-18887/com.test.measurelistviewofsliding D/FlingScroller: currY:14372
10-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




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值