Android学习之旅

Android 事件分发机制

2019-09-25  本文已影响0人  pj0579

事件分发的分析文章网上很多,自己看来比较好的文章
美团开发人员的
https://www.jianshu.com/p/e99b5e8bd67b#
郭神的
https://blog.csdn.net/guolin_blog/article/details/9153747
结合自己的理解说下
总结就是 最底部的dispatchTouchEvent一旦return true(View可点击时的dispatchTouchEvent肯定返回ture),事件就开始一层层往回传了, 最底部的view肯定有你自己写的onClick之类或者View可点击促使返回true消耗了这次事件,默认回传的时候父View不会执行自己的点击事件直接向上层返回true。

OnTouchEvent
首先ViewdispatchTouchEvent方法默认是这样的

public boolean dispatchTouchEvent(MotionEvent event) {
            // 主体是这些代码
            boolean result = false;
            ....... 
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                // 默认返回 false
                // 在一个普通的view触摸事件执行时 需要这些条件才会返回true,onTouch先于onTouchEvent执行
                result = true;
            }
           // onTouchEvent 默认返回false
           if (!result && onTouchEvent(event)) {
                result = true;
            }
        ......
        return result;
    }

dispatchTouchEvent返回true的时候 开始一级级往上传递true,当没有子 view返回true的时候,则会去执行自己的super.dispatchTouchEvent
然后子view的dispatchTouchEvent方法是他的父ViewGroup调用的。
看下ViewGroup是怎么分发事件的 代码很长 部分精简 主要看设置handled相关的办法

 public boolean dispatchTouchEvent(MotionEvent ev) {
            ...
            // 是否拦截 true的话 不会向子View传递事件
            final boolean intercepted;
            boolean handled = false;
            // 如果子View ACTION_DOWN 没有返回true ,mFirstTouchTarget 为空 直接拦截 不会到子View里去
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                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 {
                intercepted = true;
            }

            if (!canceled && !intercepted) {
                    // 开始对子view进行事件分发
                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            // 找到触摸到的子View 
                            ///////处理事件返回true的时候才会执行
                            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();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                        }
                    }
            }

            // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                // 没有对应的view则自己处理
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                // 分发事件给子view, 如果有必要取消之前的
                // 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 {
                        // MOVE事件被拦截的话 会传递CANCEL事件给子View
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                         // 判断执行子view的c还是自己的super.dispatchTransformedTouchEvent()
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                             // 子view返回true
                            handled = true;
                        }
                }
        return handled;
    }

除了这个正常的事件分发,还有一些特殊的情况没有展示,比如DOWN事件子View返回true,
MOVE事件被父View拦截的话 会造成第一个MOVE事件作为一个CANCEL事件传递给子View,之后的事件都在这个父View上被消费,等等情况还有很多。
ViewGroup的dispatch也相应返回true 直到最顶层的View的 dispatchTouchEvent。

上一篇下一篇

猜你喜欢

热点阅读