美文网首页
ViewGroup------dispatchTouchEven

ViewGroup------dispatchTouchEven

作者: 风月寒 | 来源:发表于2021-03-05 18:38 被阅读0次
dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
        ......
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Handle an initial down.
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                cancelAndClearTouchTargets(ev);
                resetTouchState();//1
            }

            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//2
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);//3
                    ev.setAction(action); 
                } else {
                    intercepted = false;//4
                }
            } else {
                intercepted = true;//5
            }
            
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            if (!canceled && !intercepted) {//6
                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(); // always 0 for down
                    ......
                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();//7
                        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);
                            newTouchTarget = getTouchTarget(child);//8
                            if (newTouchTarget != null) {
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }
                            resetCancelNextUpFlag(child);
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {//9
                                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) {//10
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);//11
            } else {
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {//12
                        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;
                }
            }
        ......
        return handled;
    }

当是down事件的时候,则会直接重置状态,在1处有一个重要的处理,即将mFirstTouchTarget置为空。

resetTouchState()
private void resetTouchState() {
        clearTouchTargets();
}

private void clearTouchTargets() {
        TouchTarget target = mFirstTouchTarget;
        if (target != null) {
            do {
                TouchTarget next = target.next;
                target.recycle();
                target = next;
            } while (target != null);
            mFirstTouchTarget = null;
        }
    }

mFirstTouchTarget关系到后面的分析。

当我们点击的时候,会先走MotionEvent.ACTION_DOWN事件,所以会进入第一个if中,然后看2处,这个值可以通过requestDisallowInterceptTouchEvent()设置,默认是false。因为为false,则继续进入到第二个if中,调用onInterceptTouchEvent()来判断是否拦截,这个时候分两种情况讨论。

onInterceptTouchEvent()返回true 表示拦截

当onInterceptTouchEvent()返回true时,intercepted则为true。则进入6处的判断。因为是取反,所以不会进去.

继续11处的判断,因为一开始mFirstTouchTarget被置为null,所以进入if中,调用dispatchTransformedTouchEvent()。

dispatchTransformedTouchEvent()
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {//1
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

      
        if (newPointerIdBits == 0) {
            return false;
        }
        ......
    
        if (child == null) {//2
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }

            handled = child.dispatchTouchEvent(transformedEvent);
        }
        
        transformedEvent.recycle();
        return handled;
    }

第一次进来,cancel为false,且不是MotionEvent.ACTION_CANCEL事件,所以不会进入dispatchTransformedTouchEvent方法中的1处。

前面传进来的child为null,所以会直接进入2处的if中。调用父类的dispatchTouchEvent,方法。而ViewGroup的父类是view,所以会进入View的dispatchTouchEvent中。然后调用onTouch--》onTouchEvent --> onClick or onLongClick,

具体细节看 https://www.jianshu.com/p/7c5bd3b97968

onInterceptTouchEvent()返回false 表示不拦截

不拦截的时候intercepted为false,则会进入6处的if。

进入之后,先根据Z值进行排序。然后寻找对应的child,找到对应的child之后,则调用dispatchTransformedTouchEvent进行分发。只有当dispatchTransformedTouchEvent返回true时,将mFirstTouchTarget赋值,并且alreadyDispatchedToNewTouchTarget置为true。否则还是不会将mFirstTouchTarget赋值。

对于此处ACTION_DOWN的处理具体体现在dispatchTransformedTouchEvent()
该方法返回boolean,如下:

true---->事件被消费----->mFirstTouchTarget!=null

false--->事件未被消费--->mFirstTouchTarget==null

因为在dispatchTransformedTouchEvent()会调用递归调用dispatchTouchEvent()和onTouchEvent()所以dispatchTransformedTouchEvent()的返回值实际上是由onTouchEvent()决定的.简单地说onTouchEvent()是否消费了Touch事件(true or false)的返回值决定了
dispatchTransformedTouchEvent()的返回值!!!!从而决定了mFirstTouchTarget是否为null!!!!!!从而进一步决定了ViewGroup是否处理Touch事件.

既不是MotionEvent.ACTION_DOWN,mFirstTouchTarget=null

mFirstTouchTarget==null表明事件未被消费,即Down并没有在子View中找到处理目标,即Down事件结束后并没有mFirstTouchTarget的值,这时也不用往下传了,下面既然Down都搞不定,Move也不需要给你处理,这时打断是true。就跟拦截一样的流程

MotionEvent.ACTION_MOVE

当为move事件的时候,当view进行分发,然后mFirstTouchTarget不为null,则进入第一个if中,继续走到3处,分为两种情况,不拦截和拦截。

当不拦截的时候,会进入6处的if,但是现在是ACTION_MOVE事件,所以不会走进7、8、9处,mFirstTouchTarget不为null不为null,进入10处的else,然后根据taget.child继续分发。(简单的理解就是,包一个工程,交给别人做且跟签了合同了,那就继续给别人做)

当拦截的时候,不会进入6,并且会将alreadyDispatchedToNewTouchTarget = false。当alreadyDispatchedToNewTouchTarget = false时,将会进入12处的else。因为intercepted = true,name cancelChaild = true,然后进入dispatchTransformedTouchEvent(),第二个参数为true,这个时候就会将设置为cancel事件,然后调用child.dispatchTouchEvent(),相当于取消子View的事件,.并且把mFirstTouchTarget设置为null.因为move事件是多次的,第二次进入的时候,直接进入5,然后跟ACTION_DOWN拦截的时候处理一样。(简单的理解就是,包一个工程,本来交给别人做了,但是发现项目方有追加钱。油水大大的,这个时候,不想给别人做,就取消跟别人的合作,然后交由自己做)

总结

1、dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止传递了(到达终点)(没有谁能再收到这个事件)。看下图中只要return true事件就没再继续传下去了,对于return true我们经常说事件被消费了,消费了的意思就是事件走到这里就是终点,不会往下传,没有谁能再收到这个事件了。

2、dispatchTouchEvent 和 onTouchEvent return false的时候事件都回传给父控件的onTouchEvent处理。

3、dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
ViewGroup 和View的这些方法的默认实现就是会让整个事件安装U型完整走完,所以 return super.xxxxxx() 就会让事件依照U型的方向的完整走完整个事件流动路径),中间不做任何改动,不回溯、不终止,每个环节都走到。

4、ACTION_DOWN事件在哪个控件消费了(return true), 那么ACTION_MOVE和ACTION_UP就会从上往下(通过dispatchTouchEvent)做事件分发往下传,就只会传到这个控件,不会继续往下传,如果ACTION_DOWN事件是在dispatchTouchEvent消费,那么事件到此为止停止传递,如果ACTION_DOWN事件是在onTouchEvent消费的,那么会把ACTION_MOVE或ACTION_UP事件传给该控件的onTouchEvent处理并结束传递。

相关文章

网友评论

      本文标题:ViewGroup------dispatchTouchEven

      本文链接:https://www.haomeiwen.com/subject/vrkdqltx.html