Android学习之View的事件分发

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36391075/article/details/81878672

对于View的事件分发,其实就是对MotionEvent对象的分发。

事件分发的顺序:

Actiivty——>Window——>ViewGroup——>View

涉及事件分发有三个特别重要的方法:

  • public boolean dispatchTouchEvent(MotionEvent ev):用来进行事件的分发,如果点击事件能够传递给当前VIew,那么此方法就一定会被调用

  • public boolean onInterceptTouchEvent(MotionEvent event):用来判断是否拦截某个事件,只存在于ViewGroup,在dispatchTouchEvent(MotionEvent ev)内部调用

  • public boolean onTouchEvent(MotionEvent event):用来处理点击事件。在dispatchTouchEvent(MotionEvent ev)方法内部调用

Activity的事件分发

当一个点击事件发生时,事件最先传到ActivitydispatchTouchEvent(MotionEvent ev)进行事件分发:

//在Activity中

 public boolean dispatchTouchEvent(MotionEvent ev) {
         //一般事件的开始都是Down事件
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            //实现屏保的功能
            onUserInteraction();
        }
        //交给window进行事件分发
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        //当所有的VIew都不处理该事件时,Activity的onTouchEvent方法调用
        return onTouchEvent(ev);
    }

    //该方法为空,当此Activity在栈顶的时候,触屏点击home,back,menu建等都会触发此方法
     public void onUserInteraction() {
    }

//在PhoneWindow中.
//在Activity的dispatchTouchEvent方法中,getWindow所得到的Winodw就是PhoneWindow,它是Window的子类
 @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
        //mecor,是顶层View,即DecorView的对象
    }

//在DecorView中
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
        //DecorView继承了FrameLayout,是所有界面的父类;
        //调用父类的方法,即调用了ViewGroup的dispatchTouchEvent方法
        //即事件交了ViewGroup去进行处理
    }

//在Activity中
//当Activity下的任何一个子View都没有接收/处理事件时,该方法被调用
public boolean onTouchEvent(MotionEvent event) {
        //判断是否是Window边界外的触摸事件
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;// 即 只有在点击事件在Window边界外才会返回true,一般情况都返回false
    }

//在Wiundow中
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
        // 主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等
        final boolean isOutside =
                event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
                || event.getAction() == MotionEvent.ACTION_OUTSIDE;
        if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
            return true;
        }
        return false;
    // 返回true:说明事件在边界外,即 消费事件
    // 返回false:未消费(默认)
    }

 private boolean isOutOfBounds(Context context, MotionEvent event) {
        final int x = (int) event.getX();
        final int y = (int) event.getY();
        final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
        final View decorView = getDecorView();
        return (x < -slop) || (y < -slop)
                || (x > (decorView.getWidth()+slop))
                || (y > (decorView.getHeight()+slop));
    }

Activity事件分发总结:

这里写图片描述

这里写图片描述

ViewGourp的事件分发

上面我们分析到,Activity会将事件分发交给ViewGroup:

//在DecorView中
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
        //DecorView继承了FrameLayout,是所有界面的父类;
        //调用父类的方法,即调用了ViewGroup的dispatchTouchEvent方法
        //即事件交了ViewGroup去进行处理
    }
     @Override
    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) {
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                //如果Action为ACTION_DOWN,那么就会对该事件做重置状态的操作
                //在下面方法中,会对FLAG_DIAALLOW_INTERCEPT进行重置,
                //因此对子View调用requstDsallowInterceptTouchEvent方法并不能影响ViewGourp对
                //ACTION_DOWN事件的处理
                resetTouchState();
            }

            // 检查是否拦截
            final boolean intercepted;
            //判断事件类型是不是为MotionEvent.ACTION_DOWN
            //或mFirstTouchTarget != null
            //mFirstTouchTarget 表示是否事件被子元素成功处理了,也就是ViewGroup是否拦截了
            //当子元素成功处理事件时,mFirstTouchTarget会被赋值并指向子元素.
            //也就是说当ViewGroup不拦截事件并将事件交给子元素时mFirstTouchTarget != null,
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                    //FLAG_DISALLOW_INTERCEPT可以通过requestDisallowInterceptTouchEvent方法来设置。
                    //一旦设置,ViewGroup将无法拦截除了ACTION_DOWN以外的其他点击事件
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                //询问是否拦截,默认为不拦截
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }

            //以上可以看出,如果ViewGourp一旦拦截事件,那么后续的事件将会默认交给它处理
            //并且不会再调用onInterceptTouchEvent(ev)方法。(除ACTION_DOWN)

            ... ...///代码省略

            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            //检查事件是否取消或被拦截
            if (!canceled && !intercepted) {

                  ... ... //代码省略

                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        //得到点击事件在子元素中的坐标
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);

                        // Find a child that can receive the event.
                        // Scan children from front to back.
                        //考虑到两个View交叉重合的情况,下面的先放进集合,
                        //但是按常理说我们手指先按到上面的,所以这里做了一个倒序
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        //倒序遍历所有的子View
                        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;
                            }
                            //判断子元素是否能够接收到点击事件:
                            //1. 子元素是否在动画
                            //2. 点击事件的坐标是否落在子元素的区域内
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                            //在getTouchTargerget方法中会遍历循环得到一个view;
                            //在一般情况下,事件还没有分发给子View时,newTouchTarget为null
                            //mFirstTouchtarget是一个单链表的
                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                // Child is already receiving touch within its bounds.
                                // Give it the new pointer in addition to the ones it is handling.
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            resetCancelNextUpFlag(child);
                            //重点
                            //dispatchTransformedTouchEvent方法中,会判断传入的child是否为null,
                            //如果不为null,就会将直接调度child.dispatchTouchEvent方法,
                            //这样事件就交给了子元素去处理。
                            //如果为null,就会调用super.dispatchTouchEvent方法,
                            //View(ViewGroup继承自View)会自行处理点击事件
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                //进此方法,说明子元素分发了事件
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                //mFistTouchTarget在addTouchTarget中赋值
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                //记录已经有子元素处理该点击事件
                                alreadyDispatchedToNewTouchTarget = true;
                                //跳出循环
                                break;
                            }

                            // The accessibility focus didn't handle the event, so clear
                            // the flag and do a normal dispatch to all children.
                            ev.setTargetAccessibilityFocus(false);
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }//循环块结束

                 if (newTouchTarget == null && mFirstTouchTarget != null) {
                        // Did not find a child to receive the event.
                        // Assign the pointer to the least recently added target.
                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }//判断是否取消和拦截的if块结束

                  ... ... //代码省略 
    }

**重点看看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) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

        // Calculate the number of pointers to deliver.
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

        // If for some reason we ended up in an inconsistent state where it looks like we
        // might produce a motion event with no pointers in it, then drop the event.
        if (newPointerIdBits == 0) {
            return false;
        }

        // If the number of pointers is the same and we don't need to perform any fancy
        // irreversible transformations, then we can reuse the motion event for this
        // dispatch as long as we are careful to revert any changes we make.
        // Otherwise we need to make a copy.
        final MotionEvent transformedEvent;
        if (newPointerIdBits == oldPointerIdBits) {
            if (child == null || child.hasIdentityMatrix()) {
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);

                    handled = child.dispatchTouchEvent(event);

                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
            transformedEvent = MotionEvent.obtain(event);
        } else {
            transformedEvent = event.split(newPointerIdBits);
        }

        // Perform any necessary transformations and dispatch.
        //进行事件分发
        if (child == null) {
            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);
        }

        // Done.
        transformedEvent.recycle();
        return handled;
    }

     private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target;
        return target;
    }

如果在遍历完子View后,ViewGroup仍然没有找到事件处理者,即ViewGroup并没有子View处理了事件或者是子View处理了事件,但是子View的dispatchTouchEvent方法返回了false(一般是由于子View的onTouchEvent方法返回了false),那么ViewGroup会去处理这个事件。

在上面代码中,如果dispatchTransformedTouchEvent这个方法返回false,那么mFistTouchTarger肯定为null,那么事件就交给ViewGroup,其实就是交给了View:

 // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } 

前面我们说过,如果一开始mFistTouchTarget不为null且Action不为ACTION_DOWN时,就说明已经有子元素处理该事件了,那么就不会再去进行拦截询问,那么它会直接进行事件分发:

else {
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary.
                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;
                }
            }

ViewGroup的事件分发总结:

这里写图片描述

这里写图片描述

View的事件分发:

从ViewGourp的事件分发,我们知道,View的事件分发也是从dispatchTouchEvent方法开始的。

public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }
        //查看是否有窗口覆盖在上面
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;

            //当View的onTouchLListener不为空
            //且View是enable状态
             //且onTouch方法返回true时,
             //result = ture

            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
            //如果result为true,那么onTouchEvent就不会调用,
            //这说明View的onTouchListener的优先级高于onTouchEvent
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

如果我们给View设置了OnTouchListener监听,并且在回调方法onTouch()中返回true,View的onTouchEvent就得不到执行,其dispatchTouchEvent方法就会直接返回true给父容器,相反如果返回false,或者没有设置OnTouchListener监听,才会执行onTouchEvent()方法对分发来的事件进行处理。

public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();

        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
        //只要View是可点击的(设置了上面三个属性其中一个),即使View的enable属性为disable,
        //View的onTouchEvent都会返回ture,即消耗此事件。
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return clickable;
        }
        //如果View设置了代理,就会执行mTouchDelegate.onTouchEvent(event)
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }
        //下面是onTouchEvent中对点击事件的具体处理
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    if ((viewFlags & TOOLTIP) == TOOLTIP) {
                        handleTooltipUp();
                    }
                    //查看是否可点击
                    if (!clickable) {
                        removeTapCallback();
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break;
                    }
                    //查看是否处理prepressed状态
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                        }

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            // This is a tap, so remove the longpress check
                            removeLongPressCallback();

                            // Only perform take click actions if we were in the pressed state
                            if (!focusTaken) {
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start.
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                //试图用Hanlder去post这个mPerformClick,
                                //PerformClick是Runnable的实现类
                                //在它的run方法中会调用performClick()
                                if (!post(mPerformClick)) {
                                //在performClick方法中,如果View设置了OnClickListener,
                                //那么perforCliick方法内部会调用它的onClick方法
                                    performClick();
                                }
                            }
                        }

                      ... ... //代码省略
                    break;

               ... ... //代码省略
            }
            //只要可以点击,就会返回ture
            return true;
        }//if块结束

        return false;
    }

     public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        notifyEnterOrExitForAutoFillIfNeeded(true);

        return result;
    }

只要View处于可点击状态,并且进入了switch的判断逻辑,就会被返回true,表明该事件被消费掉了,也就是说只要View是可点击的,事件传到了其OnTouchEvent,都会被消费掉。而平时我们在调用setOnClickListener方法给View设置点击事件监听时,都会将其点击状态修改为可点击状态。

public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

当View的onTouchEvent返回ture后,Dwon事件被消耗掉,会到ViewGroup的dispatchTouchEvent方法dispatchTransformedTouchEvent方法ture,这个时候会调用addTouchTarget方法,那么mFisrtTouchTarget就不为null了,当Move,Up事件来到的时候,

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

不成立,就直接来到下面的代码,调用dispatchTransformedTouchEvent方法继续进行分发,待子View进行消费。

View的事件分发总结:

这里写图片描述

流程梳理总结:

这里写图片描述

当我什么都不做处理,点击Button的时候:
对于ACTION的类型:
- 0:ACTION_DOWN
- 1:ACTION_UP
- 2:ACTION_MOVE

//先是DWON事件
D/MainActivity: dispatchTouchEvent: 111   0
D/FrameRoot: dispatchTouchEvent: 111   0
             onInterceptTouchEvent: 111
D/FrameRoot: onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  0
                onInterceptTouchEvent: 111
                onInterceptTouchEvent: false
D/ButtonView: dispatchTouchEvent: 111   
D/ButtonView: onTouchEvent: 111
D/ButtonView: onTouchEvent: true
              dispatchTouchEvent: true
D/RelativeRoot: dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true

//然后是MOVE事件
D/MainActivity: dispatchTouchEvent: 111   2
D/FrameRoot: dispatchTouchEvent: 111   2
            onInterceptTouchEvent: 111
            onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  2
                onInterceptTouchEvent: 111
D/RelativeRoot: onInterceptTouchEvent: false
D/ButtonView: dispatchTouchEvent: 111   
              onTouchEvent: 111
              onTouchEvent: true
              dispatchTouchEvent: true
D/RelativeRoot: dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true

//最后是UP事件
D/MainActivity: dispatchTouchEvent: 111   1
D/FrameRoot: dispatchTouchEvent: 111   1
            onInterceptTouchEvent: 111
            onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  1
                onInterceptTouchEvent: 111
                onInterceptTouchEvent: false
D/ButtonView: dispatchTouchEvent: 111   
            onTouchEvent: 111
            onTouchEvent: true
            dispatchTouchEvent: true
D/RelativeRoot: dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true

如果我在RelativeLayout中最拦截,但是没有重写onTouchEvent方法:

//DOWN事件
D/MainActivity: dispatchTouchEvent: 111   0
D/FrameRoot: dispatchTouchEvent: 111   0
            onInterceptTouchEvent: 111
            onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  0
                onInterceptTouchEvent: 111
                onInterceptTouchEvent: true
                onTouchEvent: 111
                onTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: false
D/FrameRoot: onTouchEvent: 111
            onTouchEvent: false
            dispatchTouchEvent: false
D/MainActivity: onTouchEvent: false
                dispatchTouchEvent: false
//MOVE事件                
D/MainActivity: dispatchTouchEvent: 111   2
                onTouchEvent: false
                dispatchTouchEvent: false

//UP事件 
D/MainActivity: dispatchTouchEvent: 111   1 
D/MainActivity: onTouchEvent: false
                dispatchTouchEvent: false

尽管我在RelativeLayout的onInterceptTouchEvent方法中返回ture,这个时候会调用它的onTouchEvent方法,但是我并没有重写onTouchEvent且RelativeLayout是不可点击的,所以返回false,那么RelativeLayout的dispatchTochEvent方法返回false,而且mFisrtTouchTarget == null,所以当MOVE或UP事件来到时,只会到DecorView,intercepted = true;,直接调用父类的dispatchTouchEvent方法,从DecorView就回到Activity了。

如果我在RelativeLayout中拦截,且重写onTouchEvent方法,返回ture:

//DOWN事件
D/MainActivity: dispatchTouchEvent: 111   0
D/FrameRoot: dispatchTouchEvent: 111   0
            onInterceptTouchEvent: 111
            onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  0
                onInterceptTouchEvent: 111
                onInterceptTouchEvent: true
                onTouchEvent: 111
                onTouchEvent: true
                dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true

//MOVE事件
D/MainActivity: dispatchTouchEvent: 111   2
D/FrameRoot: dispatchTouchEvent: 111   2
            onInterceptTouchEvent: 111
            onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  2
                onTouchEvent: 111
                onTouchEvent: true
D/RelativeRoot: dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true

//UP事件
D/MainActivity: dispatchTouchEvent: 111   1
D/FrameRoot:    dispatchTouchEvent: 111   1
                onInterceptTouchEvent: 111
                onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  1
                onTouchEvent: 111
                onTouchEvent: true
                dispatchTouchEvent: true
D/FrameRoot:    dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true

我们可以看到后续的事件都只传到RelativeLayout

如果我不在RelativeLayout做拦截,在onTouchEvent中返回ture,在Button在onTouchEvent中返回false:

//DOWN事件
D/MainActivity: dispatchTouchEvent: 111   0
D/FrameRoot: dispatchTouchEvent: 111   0
            onInterceptTouchEvent: 111
            onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  0
                onInterceptTouchEvent: 111
                onInterceptTouchEvent: false
D/ButtonView: dispatchTouchEvent: 111   
D/ButtonView: onTouchEvent: 111
              onTouchEvent: false
              dispatchTouchEvent: false
D/RelativeRoot: onTouchEvent: 111
D/RelativeRoot: onTouchEvent: true
                dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true

//MOVE事件
D/MainActivity: dispatchTouchEvent: 111   2
D/FrameRoot: dispatchTouchEvent: 111   2
            onInterceptTouchEvent: 111
D/FrameRoot: onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  2
                onTouchEvent: 111
                onTouchEvent: true
                dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true

//UP事件
D/MainActivity: dispatchTouchEvent: 111   1
D/FrameRoot: dispatchTouchEvent: 111   1
            onInterceptTouchEvent: 111
            onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111  1
                onTouchEvent: 111
                onTouchEvent: true
                dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true

我们可以看到,只有Down事件去向Button分发了,后面的UP事件和MOVE事件都没有,因为在RelativeLayout调用dispatchTouchEvent方法时,mFisrtTouchTarget 为null,直接调用父类的dispatchTouchEvent方法,然后调用了onTouchEvent方法。

猜你喜欢

转载自blog.csdn.net/qq_36391075/article/details/81878672