Android Touch事件总结 二 (双指实现RecyclerView的快速滚动)

6 篇文章 0 订阅
3 篇文章 0 订阅

说明:本博客为原创,转载请注明出处 CSDN-ANDROID笔记栈
由于作者水平有限,错误在所难免,请见谅,可以留言,本人会及时改正


索引


Android Touch事件总结 一 中介绍了Android Touch事件的分发,拦截,消费。
本篇是对总结一的补充和实战。

MotionEvent

在分发、拦截、消费方法中传入的参数都是MotionEvent对象,所以有必要对MotionEvent类进行分析,大家可以参考这篇博文:Android MotionEvent详解

我重点强调下MotionEvnet的几个方法在多指触摸事件中的作用:

getActionIndex:在ACTION_POINTER_DOWN和ACTION_POINTER_UP事件中返回索引

getPointerId:通过索引获取id

findPointerIndex:通过id获取索引

pointerId在整个触摸事件[DOWN->UP]中是不变的,但是index可能会变。


Demo

GitHub地址: GitHub
环境: Windows7+JAVA8
IDE: AndroidStdio2.2.2
compileSdkVersion:24
测试设备:Nexus5(6.0.1)

运行结果截图:
多指快递滚动
黑色和黑色线是手指的移动轨迹,30%是整个移动轨迹相对于View整个高度的百分比(向下滑动>0,向上滑动<0)
只有双指滑动在支持快递滚动(RecyclerView中的Touch事件被拦截),单指没有效果(RecyclerView正常滚动)

下面是核心代码:

@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
    switch (ev.getActionMasked())
    {
        case MotionEvent.ACTION_DOWN:
        {
            resetFinger(); // 重置手指状态

            final int pointerId = ev.getPointerId(ev.getActionIndex()); // 获取当前手指(单手指)的id

            final int index = ev.findPointerIndex(pointerId);

            mFirstFinger = new Finger(pointerId); // 初始化第一根手指

            TouchPoint touchPoint = new TouchPoint(ev.getX(index), ev.getY(index)); // 保存当前的坐标x&y值
            // 这边需要说明的是调用ArrayList.add(MotionEvent) 之后整个List中的值都会被最新添加的对象给覆盖!未查明原因,有知道原因的请告知,谢谢。
            mFirstFinger.add(touchPoint); // 添加到ArrayList中

            mFirstFinger.setTargetPoint(touchPoint);

                break;
        }
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_OUTSIDE:
        {
            mFingerTouchMode = false; // 标志位,用于什么时候拦截Touch事件

            resetFinger(); // 重置手指

            break;
        }
        case MotionEvent.ACTION_MOVE:
        {
            if (mFirstFinger != null)
            {
                final int index = ev.findPointerIndex(mFirstFinger.id);

                TouchPoint touchPoint = new TouchPoint(ev.getX(index), ev.getY(index));

                mFirstFinger.add(touchPoint); // 添加手指移动轨迹

                if (mSecondFinger == null)
                {
                    mFirstFinger.setTargetPoint(touchPoint);
                }
            }

            if (mSecondFinger != null) // 如果是多指状态,添加第二根手指的移动轨迹
            {
                final int index = ev.findPointerIndex(mSecondFinger.id);

                TouchPoint touchPoint = new TouchPoint(ev.getX(index), ev.getY(index));

                mSecondFinger.add(touchPoint);
            }

            if (mFirstFinger != null && mSecondFinger != null) // 如果是多指状态,则计算移动距离百分比
            {
                if (Math.abs(mFirstFinger.touchTime - mSecondFinger.touchTime) <= FINGER_TOUCH_DURATION) // 两根手指的触摸时间<500毫秒
                {
                    int fd = mFirstFinger.getDistanceFromTargetPoint(); // 计算第一根手指的移动距离

                    int sd = mSecondFinger.getDistanceFromTargetPoint(); // 计算第二根手指的移动距离

                    if (fd * sd > 0) // 两根手指同向
                    {
                        mFingerTouchMode = true;

                        int dDelta = (fd + sd) / 2;

                        mPercent = Math.min((int) (dDelta * 100f * mScale / getHeight()), 100); // 计算百分比

                       if (mCallback != null)
                       {
                        mCallback.onFingerScrolling(mPercent);//  触发回调事件
                       }
                      }
                    }
                }

                break;
            }
            case MotionEvent.ACTION_POINTER_DOWN: // 多指状态
            {
                if (mSecondFinger == null) // 初始化第二根手指
                {
                    final int pointerId = ev.getPointerId(ev.getActionIndex());

                    final int index = ev.findPointerIndex(pointerId);

                    mSecondFinger = new Finger(pointerId);

                    TouchPoint touchPoint = new TouchPoint(ev.getX(index), ev.getY(index));

                    mSecondFinger.add(touchPoint);

                    mSecondFinger.setTargetPoint(touchPoint);

                    if (mCallback != null)
                    {
                        mCallback.onFingerStart();
                    }
                }

                break;
            }
            case MotionEvent.ACTION_POINTER_UP:
            {
                mFingerTouchMode = ev.getPointerCount() > 2; // 判断当前是不是多指状态

                final int pointerUpId = ev.getPointerId(ev.getActionIndex()); // 获取当前是哪根手指触发了UP事件

                if (mFirstFinger != null && pointerUpId == mFirstFinger.id) // 如果是第一根手指,交换手指顺序
                {
                    mFirstFinger = mSecondFinger;

                    mFirstFinger.resetTargetPoint();

                    mSecondFinger = null;
                }
                else if (mSecondFinger != null && pointerUpId == mSecondFinger.id)
                {
                    if (mFirstFinger != null) mFirstFinger.resetTargetPoint();

                    mSecondFinger = null;
                }

                if (mCallback != null)
                {
                    mCallback.onFingerEnd();
                }

                mPercent = 0;

                break;
            }
        }

        if (!mFingerTouchMode) // 如果是当前是多指滑动状态,拦截Touch事件,这里是没有调用super方法(不会分发该事件)
        // onInterceptionTouchEvent 只能拦截当前是DOWN事件或者是FirstTouchTarget对象不为空的时候!什么时候不为空,还没有搞明白!
        {
            super.dispatchTouchEvent(ev);
        }

        invalidate(); // 把移动轨迹在onDraw中画出来

        return true;
    }

xml布局

<com.neulion.android.dl.touchdemo.widget.DLMultipleFingerScrollLayout
            android:id="@+id/dl_multiple_finger_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#ffffff">

        <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

    </com.neulion.android.dl.touchdemo.widget.DLMultipleFingerScrollLayout>

具体的实现可以在GitHub上下载源码。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值