多点触控事件;
PointerId 和 Index 区别:
PointerId
每根手指从按下、移动到离开屏幕,每个手指都会拥有一个固定PointerId,PointerId的值可以是任意的值。
PointerIndex
每根手指从按下、移动到离开屏幕,每根手指在每一个事件的Index可能是不固定的,因为受到其它手指的影响。比如,A跟B两根手指同时按在屏幕上,此时A的PointerIndex为0,B的则为1.当A先离开屏幕时,B的PointerIndex则变为了0. 但是,PointerIndex的值的不是任意的,它必须在[0,PointerCount-1]的范围内。其中PointerCount为参与触控的手指数量。
PointerId是固定不变的,每个手指是拥有一个固定PointerId; PointerIndex是会变化的;
//参数中的 pointerIndex 就是 actionIndex
int id = event.getPointerId(event.getActionIndex());
....
//在按下触发按键时候记录PointerId
pointerIdHashMap.put(id, temp.key);
此处代码是只是onTouchEvent的部分
private void onTouchEvent(View view, MotionEvent event) {
// 编辑模式
if (isEditMode) {
setNewPositionByEvent(event);
return;
}
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
int id = event.getPointerId(event.getActionIndex());
//获取是否在按键上
AreaInfo temp = getAreaInfoByEvent(event);
if (temp != null) {
if (!TextUtils.isEmpty(temp.key)) {
// Logger.e(action + " id: " + id + ":" + temp.key);
pointerIdHashMap.put(id, temp.key);
setSelectedState(temp.key, true);
set.add(temp.key);
// TODO: 2019/12/11 触发 连击和锁定
int value = getExtendType(temp.key);
if ((value & ParamInfo.TYPE_SERIES) == ParamInfo.TYPE_SERIES) {
if (turbSet.contains(temp.key)) {
turbSet.remove(temp.key);
} else {
turbSet.add(temp.key);
}
if (!handler.hasMessages(MESSAGE_TURB_CLICK)) {
handler.sendEmptyMessage(MESSAGE_TURB_CLICK);
}
} else if ((value & ParamInfo.TYPE_LOCK) == ParamInfo.TYPE_LOCK) {
if (lockSet.contains(temp.key)) {
lockSet.remove(temp.key);
} else {
lockSet.add(temp.key);
}
}
if ((value & ParamInfo.TYPE_SLIDE_OUT_TRIGGER) == ParamInfo.TYPE_SLIDE_OUT_TRIGGER) {
downX = event.getRawX();
downY = event.getRawY();
} else if ((value & ParamInfo.TYPE_LONGPRESS_TRIGGER) == ParamInfo.TYPE_LONGPRESS_TRIGGER) {
downTime = System.currentTimeMillis();
}
bitValue &= directionMask;
bitValue = adjustPressState(bitValue);
iNativeCode.inputNativeKey(0, 0, bitValue);
}
}
break;
case MotionEvent.ACTION_MOVE:
id = event.getPointerId(event.getActionIndex());
String key = pointerIdHashMap.get(id);
temp = getAreaInfoByEvent(event);
if (temp != null) {
if (!TextUtils.isEmpty(temp.key)) {
if (TextUtils.isEmpty(key)) {
set.add(temp.key);
setSelectedState(temp.key, true);
bitValue &= directionMask;
// 处理锁定集合
bitValue = adjustPressState(bitValue);
iNativeCode.inputNativeKey(0, 0, bitValue);
pointerIdHashMap.put(id, temp.key);
} else if (!TextUtils.equals(temp.key, key)) {
set.add(temp.key);
setSelectedState(temp.key, true);
bitValue &= directionMask;
// 处理锁定集合
bitValue = adjustPressState(bitValue);
iNativeCode.inputNativeKey(0, 0, bitValue);
}
}
}
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
id = event.getPointerId(event.getActionIndex());
key = pointerIdHashMap.get(id);
// Logger.e(action + " id: " + id + ":" + key);
if (!TextUtils.isEmpty(key)) {
setSelectedState(key, false);
set.remove(key);
pointerIdHashMap.remove(id);
if (pointerIdHashMap.size() == 0) {
for (String a : set) {
setSelectedState(a, false);
}
set.clear();
}
// TODO: 2019/12/11 抬起时触发 连击和锁定操作
int value = getExtendType(key);
if ((value & ParamInfo.TYPE_CLICK_TRIGGER) == ParamInfo.TYPE_CLICK_TRIGGER) {
if ((value & ParamInfo.TYPE_LOCK) == ParamInfo.TYPE_LOCK) {
setSelectedState(key, lockSet.contains(key));
}
} else if ((value & ParamInfo.TYPE_LONGPRESS_TRIGGER) == ParamInfo.TYPE_LONGPRESS_TRIGGER) {
if ((value & ParamInfo.TYPE_SERIES) == ParamInfo.TYPE_SERIES) {
turbSet.remove(key);
} else if ((value & ParamInfo.TYPE_LOCK) == ParamInfo.TYPE_LOCK) {
if (downTime > 0 && System.currentTimeMillis() - downTime < 1000) {
lockSet.remove(key);
} else if (downTime > 0 && System.currentTimeMillis() - downTime >= 1000) {
setSelectedState(key, true);
}
}
} else if ((value & ParamInfo.TYPE_SLIDE_OUT_TRIGGER) == ParamInfo.TYPE_SLIDE_OUT_TRIGGER) {
if (downY > 0 && event.getRawY() - downY < 50 * density) {
if ((value & ParamInfo.TYPE_SERIES) == ParamInfo.TYPE_SERIES) {
turbSet.remove(key);
} else if ((value & ParamInfo.TYPE_LOCK) == ParamInfo.TYPE_LOCK) {
lockSet.remove(key);
}
} else if (downY > 0 && event.getRawY() - downY >= 50 * density) {
if ((value & ParamInfo.TYPE_LOCK) == ParamInfo.TYPE_LOCK) {
setSelectedState(key, true);
}
}
}
}
bitValue &= directionMask;
bitValue = adjustPressState(bitValue);
iNativeCode.inputNativeKey(0, 0, bitValue);
break;
}
}
触摸点在View中坐标的时间现象;
1、onInterceptTouchEvent()是用于处理事件(类似于预处理,当然也可以不处理)并改变事件的传递方向,也就是决定是否允许Touch事件继续向下(子控件)传递,一但返回True(代表事件在当前的viewGroup中会被处理),则向下传递之路被截断(所有子控件将没有机会参与Touch事件),同时把事件传递给当前的控件的onTouchEvent()处理;返回false,则把事件交给子控件的onInterceptTouchEvent()
2、onTouchEvent()用于处理事件,返回值决定当前控件是否消费(consume)了这个事件,也就是说在当前控件在处理完Touch事件后,是否还允许Touch事件继续向上(父控件)传递,一但返回True,则父控件不用操心自己来处理Touch事件。返回true,则向上传递给父控件(注:可能你会觉得是否消费了有关系吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。)
3、dispatchTouchEvent(事件的分发)
####1、对以上方法均不作处理,都返回super。这意味着我们既不拦截,也不消费。
结合流程图,不难发现,如果我对事件既不拦截,也不消费,当触发ACTION_DOWN的时候,事件会经过Activity——MyViewGroupA——MyViewGroupB——MyView一层层的向下进行dispatchTouchEvent(分发)—onInterceptTouchEvent(拦截)调用。当到达最底层MyView后,开始触发消费操作,因为我均不消费,ACTION_DOWN将由底层一层层向上冒,移交上层处理。当抵达最上层Activity后,说明下层均不消费,之后触发的ACTION_MOVE和ACTION_UP将不再向下层分发传递,直接交由Activity分发给自己进行处理。
####2、我们将MyVIewGroupB的onInterceptTouchEvent返回值改为true,其他均是super。这意味着仅仅MyViewGroupB进行事件拦截,但均无消费
当触发ACTION_DOWN的时候,事件依然是从Activity开始一层层向下传递,当传递到MyViewGroupB时,因为进行了事件拦截,所以执行完onInterceptTouchEvent后不再向下传递,而是直接交由MyViewGroupB的onTouchEvent进行消费处理。由于我们是只拦截,不消费,所以事件向上传递,交由上层处理,最终回到Activity。之后触发的ACTION_MOVE和ACTION_UP也不再向下传递,直接交由Activity分发给自己处理。
####3、我们还是将MyViewGroupB的onInterceptTouchEvent返回super,但是将他的onTouchEvent返回true。这意味着我们不拦截,但是由MyViewGroupB进行事件处理。
可以看出,当触发ACTION_DOWN的时候,事件的分发传递过程和1的时候一样,从Activity开始一层层向下传递,最终传递到最底层MyView,触发消费操作,然后MyView将消费操作移交上层处理,然后到达MyViewGroupB的onTouchEvent,并且进行了消费处理,事件处理到此不在向上移交。当触发ACTION_MOVE和ACTION_UP操作时,事件依然需要由Activity开始向下分发传递,但是当传递到MyViewGroupB后,由于其消费了ACTION_DOWN,事件将不再继续向下分发,而是直接由MyViewGroupB分发给自己的onTouchEvent进行继续处理。事件处理也不再向上移交。
####4、将MyViewGroupB的onInterceptTouchEvent和onTouchEvent的返回值均改为true。这意味着既拦截,又消费。
当触发ACTION_DOWN的时候,依然从Activity开始向下传递,当到达MyViewGroupB的是,因为在onInterceptTouchEvent进行了拦截操作,因此不再继续向下分发传递,而是交由MyViewGroupB的onTouchEvent进行处理消费。MyViewGroupB的onTouchEvent返回的是true,说明它决定对ACTION_DOWN进行处理,因此事件也就不再移交上层处理。当触发ACTION_MOVE和ACTION_UP的时候,事件还是从Activity开始向下传递,当到达MyViewGroupB的时候,由于之前进行了拦截操作,因此,MyViewGroupB直接将事件分发给自己的onTouchEvent进行处理,不在向下分发传递。事件处理也不再向上层移交。