说明:本博客为原创,转载请注明出处 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上下载源码。