Android基础-多点触控和事件传递

88 阅读5分钟

多点触控事件;

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中坐标的时间现象;

image.png

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(事件的分发)

onInterceptTouchEvent、onTouchEvent  

####1、对以上方法均不作处理,都返回super。这意味着我们既不拦截,也不消费。 image.png image.png

结合流程图,不难发现,如果我对事件既不拦截,也不消费,当触发ACTION_DOWN的时候,事件会经过Activity——MyViewGroupA——MyViewGroupB——MyView一层层的向下进行dispatchTouchEvent(分发)—onInterceptTouchEvent(拦截)调用。当到达最底层MyView后,开始触发消费操作,因为我均不消费,ACTION_DOWN将由底层一层层向上冒,移交上层处理。当抵达最上层Activity后,说明下层均不消费,之后触发的ACTION_MOVE和ACTION_UP将不再向下层分发传递,直接交由Activity分发给自己进行处理。

####2、我们将MyVIewGroupB的onInterceptTouchEvent返回值改为true,其他均是super。这意味着仅仅MyViewGroupB进行事件拦截,但均无消费 image.png image.png

当触发ACTION_DOWN的时候,事件依然是从Activity开始一层层向下传递,当传递到MyViewGroupB时,因为进行了事件拦截,所以执行完onInterceptTouchEvent后不再向下传递,而是直接交由MyViewGroupB的onTouchEvent进行消费处理。由于我们是只拦截,不消费,所以事件向上传递,交由上层处理,最终回到Activity。之后触发的ACTION_MOVE和ACTION_UP也不再向下传递,直接交由Activity分发给自己处理。

####3、我们还是将MyViewGroupB的onInterceptTouchEvent返回super,但是将他的onTouchEvent返回true。这意味着我们不拦截,但是由MyViewGroupB进行事件处理。 image.png image.png

可以看出,当触发ACTION_DOWN的时候,事件的分发传递过程和1的时候一样,从Activity开始一层层向下传递,最终传递到最底层MyView,触发消费操作,然后MyView将消费操作移交上层处理,然后到达MyViewGroupB的onTouchEvent,并且进行了消费处理,事件处理到此不在向上移交。当触发ACTION_MOVE和ACTION_UP操作时,事件依然需要由Activity开始向下分发传递,但是当传递到MyViewGroupB后,由于其消费了ACTION_DOWN,事件将不再继续向下分发,而是直接由MyViewGroupB分发给自己的onTouchEvent进行继续处理。事件处理也不再向上移交。

####4、将MyViewGroupB的onInterceptTouchEvent和onTouchEvent的返回值均改为true。这意味着既拦截,又消费。 image.png image.png

当触发ACTION_DOWN的时候,依然从Activity开始向下传递,当到达MyViewGroupB的是,因为在onInterceptTouchEvent进行了拦截操作,因此不再继续向下分发传递,而是交由MyViewGroupB的onTouchEvent进行处理消费。MyViewGroupB的onTouchEvent返回的是true,说明它决定对ACTION_DOWN进行处理,因此事件也就不再移交上层处理。当触发ACTION_MOVE和ACTION_UP的时候,事件还是从Activity开始向下传递,当到达MyViewGroupB的时候,由于之前进行了拦截操作,因此,MyViewGroupB直接将事件分发给自己的onTouchEvent进行处理,不在向下分发传递。事件处理也不再向上层移交。