浅析事件分发

首先需要明确事件只有一个,处理事件的view只有一个,其次是事件针对的是viewGroup才回去实现分发

Android的事件分为:

ACTION_DOWN:手指初次接触到屏幕触发

ACTION_MOVE:手指在屏幕上滑动时触发,会多次触发

ACTION_UP:手指离开屏幕时触发

ACTION_CANCEL:事件被上层拦截时触发

ACTION_POINTER_DOWN:第二根至第n根手指接触到屏幕时触发 

ACTION_POINTER_UP:第n根到第二根手指接触到屏幕时触发

(ps:Android最大支持的手指为32根)

不管是view还是viewGroup都是需要对事件的处理,处理事件的方法在dispatchTouchEvent里面

整个事件的处理流程为onTouch→onTounchEvent→onClick下图为分发流程:

我们可以看到首先当我们的事件是先进入了activity的dispatchTouchEvent方法,然后再一直分发到viewGroup的dispatchTouchEvent,当viewGroup需要处理事件的时候就会去调用super.dispatchTouchEvent,也就是view.dispatchTouchEvent

OnInterceptTouchEvent的功能是拦截事件,不去做分发,拦截的方法:内部拦截法(子view拦截),外部拦截法(父容器拦截)

当我们在这个地方进行了返回true的操作,事件就会把后续的所有事件交给当前拦截的view/viewgroup的onTouchEvent来处理,然后之前接收到事件的所有view都会收到cancel事件来停止之前的触摸事件

接下来是viewGroup里面的事件分发流程:

第一段代码:判断是否拦截子view

final boolean intercepted;

if (actionMasked == MotionEvent.ACTION_DOWN  || mFirstTouchTarget != null) {

    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

    if (!disallowIntercept) {

           intercepted = onInterceptTouchEvent(ev);

        ev.setAction(action);

    } else {

        intercepted = false;

    }

} else {

    intercepted = true;

}

我们可以看到我们是通过onInterceptTouchEvent方法来拦截和放开事件分发的,当我们的返回值为true的时候第二段代码直接不会执行,但是当onInterceptTouchEvent方法为false的时候,依然不会去分发事件,因为这个事件的分发被覆盖(阻止)

第二段代码:分发事件给子view,判断哪个子view处理事件,如果child处理事件,会设置一些变量

if (!canceled && !intercepted) {

                View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null;

                if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                    final int actionIndex = ev.getActionIndex();

                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS;

                    removePointersFromTouchTargets(idBitsToAssign);

                    final int childrenCount = mChildrenCount;

                    if (newTouchTarget == null && childrenCount != 0) {

                        final float x = isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);

                        final float y = isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);

                        final ArrayList preorderedList = buildTouchDispatchChildList();

                        final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();

                        final View[] children = mChildren;

                        for (int i = childrenCount - 1; i >= 0; i--) {

                            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);

                            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);

                            if (childWithAccessibilityFocus != null) {

                                if (childWithAccessibilityFocus != child) {

                                    continue;

                                }

                                childWithAccessibilityFocus = null;

                                i = childrenCount - 1;

                            }

                            if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) {

                                ev.setTargetAccessibilityFocus(false);

                                continue;

                            }

                            newTouchTarget = getTouchTarget(child);

                            if (newTouchTarget != null) {

                                newTouchTarget.pointerIdBits |= idBitsToAssign;

                                break;

                            }

                            resetCancelNextUpFlag(child);

                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

                                mLastTouchDownTime = ev.getDownTime();

                                if (preorderedList != null) {

                                    for (int j = 0; j < childrenCount; j++) {

                                        if (children[childIndex] == mChildren[j]) {

                                            mLastTouchDownIndex = j;

                                            break;

                                        }

                                    }

                                } else {

                                    mLastTouchDownIndex = childIndex;

                                }

                                mLastTouchDownX = ev.getX();

                                mLastTouchDownY = ev.getY();

                                newTouchTarget = addTouchTarget(child, idBitsToAssign);

                                alreadyDispatchedToNewTouchTarget = true;

                                break;

                            }

                            ev.setTargetAccessibilityFocus(false);

                        }

                        if (preorderedList != null) preorderedList.clear();

                    }

                    if (newTouchTarget == null && mFirstTouchTarget != null) {

                        newTouchTarget = mFirstTouchTarget;

                        while (newTouchTarget.next != null) {

                            newTouchTarget = newTouchTarget.next;

                        }

                        newTouchTarget.pointerIdBits |= idBitsToAssign;

                    }

                }

            }

第三段代码:执行事件

if (mFirstTouchTarget == null) {

                handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);

            } else {

                TouchTarget predecessor = null;

                TouchTarget target = mFirstTouchTarget;

                while (target != null) {

                    final TouchTarget next = target.next;

                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {

                        handled = true;

                    } else {

                        final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;

                        if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {

                            handled = true;

                        }

                        if (cancelChild) {

                            if (predecessor == null) {

                                mFirstTouchTarget = next;

                            } else {

                                predecessor.next = next;

                            }

                            target.recycle();

                            target = next;

                            continue;

                        }

                    }

                    predecessor = target;

                    target = next;

                }

            }

可以看到第三段代码里面的if是当前view的执行事件的处理,else是子view的执行事件的处理

这里表示了viewGroup(源码)的事件分发(三段代码的解析),然而对于我们多指操作时,还需要获取手指下标或者说手指的编号,接下来我们来看对于事件分发我们需要做的是什么,首先我们要明确三个方法

dispatchTouchEvent(MotionEvent ev) 分发事件

onTouchEvent(MotionEvent event) 处理事件

onInterceptTouchEvent(MotionEvent ev) 拦截事件

当我们在A ViewGroup里进行拦截的时候,系统会将A ViewGroup的子view的Touch事件全部拦截,之前的View/ViewGroup会收到cancel事件,然后再把之后发给A ViewGroup的事件全部交给A ViewGroup的TouchView,当我们不进行拦截的时候,会先到ViewGroup的dispatchTouchEvent方法,然后再到子view的dispatchTouchEvent方法,然后再到处理事件的onTouchEvent里,所以当A→B→C→D时,ABC都为ViewGroup,D为最上层的子view,事件的传递为A dispatchTouchEvent→B dispatchTouchEvent→C dispatchTouchEvent→D dispatchTouchEvent→D onTouchEvent,这里还需要注意一个问题,就是当D重写了onTouchEvent并且还在这里消费了事件才会到D为止,如果D并没有消费事件而是使用的spuer那事件的传递为A dispatchTouchEvent→B dispatchTouchEvent→C dispatchTouchEvent→D dispatchTouchEvent→D onTouchEvent→C onTouchEvent→B onTouchEvent→A onTouchEvent→Activity(即上层的view和window,详情见上图activity的分发流程) onTouchEvent,activity默认是不进行消费的也就是这个事件其实并没有被消费,所以在这个过程中一直不会被中断传递,当新来的事件再次传递回了activity的onTouchEvent,那这个事件发生的事情就不会再次被回调,这时touch事件会一直在activity进行消费,而不会被传递下去,最后截至在activity,然后再次进行分发直到有view来处理接下来的事件,那onInterceptTouchEvent(MotionEvent ev)的执行在哪里呢,是在dispatchTouchEvent(MotionEvent ev)分发事件之后,也就是说其实事件传递的流程应该是A dispatchTouchEvent→A onInterceptTouchEvent→B dispatchTouchEvent→B onInterceptTouchEvent→C dispatchTouchEvent→C onInterceptTouchEvent→D dispatchTouchEvent→D onTouchEvent→C onTouchEvent→B onTouchEvent→A onTouchEvent→Activity(即上层的view和window,详情见上图activity的分发流程) onTouchEvent

你可能感兴趣的:(浅析事件分发)