转载请注明出处 http://blog.csdn.net/a992036795/article/details/51698023

本文将讲述android点击事件的分发过程

我的上一篇文章讲述了android点击事件的来源,本文接着讲述当点击事件传输到Activity之后 分发的过程是什么样的。
上一篇文章链接: http://blog.csdn.net/a992036795/article/details/51690303

通过上一篇文章我们知道,事件最终会通过activity分发到PhoneWindow再到DecorView最后到他的子View。

那我们就从Activity的dispatchTouchEvent方法看起吧。
Activity#dispatchTouchEvent

 public boolean dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {            onUserInteraction();        }        //调用 phoneWindow的 superDiapatchTouchEvent        if (getWindow().superDispatchTouchEvent(ev)) {            return true;        }        // 如果phoneWindow#superDiapatchTouchEvent为false ,      //  则会调用Activity的 onTouchEvent        return onTouchEvent(ev);    }

从这段代码可以看出:activity把事件交给了 phoneWindow,向下传递。
如果下层没有处理这个事件,那么activity将调用自己的onTouchEvent来处理这个事件。

我们接看PhoneWindow的superDispatchTouchEvent
PhoneWindow#superDispatchTouchEvent

@Override    public boolean superDispatchTouchEvent(MotionEvent event) {        //传递给DecorView        return mDecor.superDispatchTouchEvent(event);    }

它将事件传递给了DecorView。
DecorView的superDispatchTouchEvent

public boolean superDispatchTouchEvent(MotionEvent event) {            return super.dispatchTouchEvent(event);        }

它这里调用了super.dispatchTouchEvent(event),实际就是调用了ViewGoup的
dispatchTouchEvent方法。
ViewGroup#dispatchTouchEvent

 @Override    public boolean dispatchTouchEvent(MotionEvent ev) {       ...            if (actionMasked == MotionEvent.ACTION_DOWN) {         cancelAndClearTouchTargets(ev);                // 1、清除 了disallowIntercept  标记                resetTouchState();            }            // Check for interception.            final boolean intercepted;         // 2 、判断是否拦截,这个if语句actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null         // 在ACTION_DOWN 或者 mFirstTouchTarget!=null的时候进入         // 通过下文分析会得出:如果不拦截事件,将事件交由子View处理的//mFirstTouchTarget 不会被赋值,也就是mFirstTouchTarget!=null 不成立。//那么就会有一条结论:当ViewGroup拦截事件后,那么在这个事件序列中,//将不会进入onInterceptTouchEvent(ev)判断,而是直接交由ViewGroup自身处理。        //原因是如果拦截了,下个事件不可能是ACTION_DOWN,并且mFirstTouchTarget==null ,所以上述结论成立!            if (actionMasked == MotionEvent.ACTION_DOWN                    || mFirstTouchTarget != null) {                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;                //3、这个标示可以通过requestDisallowInterceptTouchEvent                //进行设置,这个标志将影响事件的拦截,即如果这个设了这个标志,                //ViewGroup将不拦截事件,但这个对ACTION_DOWN无效,                //原因在于ACTION_DOWN时 会清楚标志,看1号注释                if (!disallowIntercept) {                    intercepted = onInterceptTouchEvent(ev);                    ev.setAction(action); // restore action in case it was changed                } else {                    intercepted = false;                }            } else {                intercepted = true;            }            // If intercepted, start normal event dispatch. Also if there is already            // a view that is handling the gesture, do normal event dispatch.            if (intercepted || mFirstTouchTarget != null) {                ev.setTargetAccessibilityFocus(false);            }            // Check for cancelation.            final boolean canceled = resetCancelNextUpFlag(this)                    || actionMasked == MotionEvent.ACTION_CANCEL;            // Update list of touch targets for pointer down, if needed.            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;            TouchTarget newTouchTarget = null;            boolean alreadyDispatchedToNewTouchTarget = false;            // 4、如果不拦截将事件往下传递            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(); // always 0 for down                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)                            : TouchTarget.ALL_POINTER_IDS;                    // Clean up earlier touch targets for this pointer id in case they                    // have become out of sync.                    removePointersFromTouchTargets(idBitsToAssign);                    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.                        final ArrayList preorderedList = buildOrderedChildList();                        final boolean customOrder = preorderedList == null                                && isChildrenDrawingOrderEnabled();                        final View[] children = mChildren;                        // 5、遍历子View                        for (int i = childrenCount - 1; i >= 0; i--) {                            final int childIndex = customOrder                                    ? getChildDrawingOrder(childrenCount, i) : i;                            final View child = (preorderedList == null)                                    ? children[childIndex] : preorderedList.get(childIndex);                            if (childWithAccessibilityFocus != null) {                                if (childWithAccessibilityFocus != child) {                                    continue;                                }                                childWithAccessibilityFocus = null;                                i = childrenCount - 1;                            }                            if (!canViewReceivePointerEvents(child)                                    || !isTransformedTouchPointInView(x, y, child, null)) {                                ev.setTargetAccessibilityFocus(false);                                continue;                            }                            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);                            // 6、这个方法将事件分发给子View (即调用子View的 disatchTouchEvent犯法)                            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();                                // 7、对mFirstTouchEvent进行赋值                                newTouchTarget = addTouchTarget(child, idBitsToAssign);                                alreadyDispatchedToNewTouchTarget = true;                                break;                            }                            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;                    }                }            }            //8、如果父控件没有子View 或者子View的 disPatchTouchEvent返回fasle ,            //即没有子View处理事件的话,将会走这个if分支            if (mFirstTouchTarget == null) {                // No touch targets so treat this as an ordinary view.                handled = dispatchTransformedTouchEvent(ev, canceled, null,                        TouchTarget.ALL_POINTER_IDS);            } 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 {                    // 9、如果子View正在处理事件,而此时ViewGroup的onIntercepted返回true,                    //此时ViewGroup就会偷取事件。                    //这个分支会回收target,将重新使得mFirstTouchEvent为null,                    //并且子View会受到一个Cancel事件                        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;                }            }            // Update list of touch targets for pointer up or cancel, if needed.            if (canceled                    || actionMasked == MotionEvent.ACTION_UP                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {                resetTouchState();            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {                final int actionIndex = ev.getActionIndex();                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);                removePointersFromTouchTargets(idBitsToRemove);            }        }        if (!handled && mInputEventConsistencyVerifier != null) {            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);        }        return handled;    }

这个方法代码比较长,内容也比较多。我们先看第一个注释的代码:

  private void resetTouchState() {        clearTouchTargets();        resetCancelNextUpFlag(this);        //清楚标志位        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;        mNestedScrollAxes = SCROLL_AXIS_NONE;    }

看以看到如果是ACTION_DOWN事件的话,它会清楚FLAG_DISALLOW_INTERCEPT这个标志

接着看第二个注释的地方:

  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;            }

这块代码,注释中已经说得差不多了。2条结论:
1、如果ViewGroup决定拦截事件,那么将不会再进入这个分支判断,后续的事件将都交由它处理。
2、可以使用 requestDisallowInterceptTouchEvent,使得ViewGroup不拦截事件,但为ACTION_DOWN事件无效。

接着会遍历所有的子View 并调用dispatchTransformedTouchEvent进行事件分发,我们来看这个方法的代码:

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,            View child, int desiredPointerIdBits) {        final boolean handled;         // 处理cancle 事件        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());            }        //传递给子View            handled = child.dispatchTouchEvent(transformedEvent);        }        // Done.        transformedEvent.recycle();        //返回结果        return handled;    }

这个方法,将会进行事件分发,如果传入的child不为null,则会传递给子View,调用子View的dispatchTouchEvent。

如果child为null 则会掉用 super.dispatchTouchEvent。当然ViewGroup的 父类是View 所以会执行view 的dispatchTouchEvent,这时就会调用ViewGroup的onTouchEvent了。

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)) {            //noinspection SimplifiableIfStatement            ListenerInfo li = mListenerInfo;            //如果 mOnTouchListener.onTouch()返回 true,则不会调用            //onTouchEvent,这里mOnTouchListener的优先级比较高            if (li != null && li.mOnTouchListener != null                    && (mViewFlags & ENABLED_MASK) == ENABLED                    && li.mOnTouchListener.onTouch(this, event)) {                result = true;            }        // 执行 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的onTouchEvent或者 自身的onTouchEvent了。

我们回过头,在看一下ViewGroup#dispatchTouchEvent这个方法中,注释7~9,看看它是怎么传递事件到自身 onTouchEvent的,和如果给mFirstTouchEvent赋值的。

我们先来看注释7、addTouchTarget方法是怎么赋值的。

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

在看第8条注释,如果mFirstTouchEvent==null 说明没有子View处理事件,这时将向上冒泡,那我们来看看它怎么处理的。

 // Dispatch to touch targets.            if (mFirstTouchTarget == null) {                // No touch targets so treat this as an ordinary view.                //调用这个方法,传入的child ==null                handled = dispatchTransformedTouchEvent(ev, canceled, null,                        TouchTarget.ALL_POINTER_IDS);

这是将调用dispatchTransformedTouchEvent 并传入的child==null,那么将会执行
dispatchTransformedTouchEvent 方法中的
handled = super.dispatchTouchEvent(event);
这个上文已经分析过了。

 if (child == null) {                handled = super.dispatchTouchEvent(event);            } else {                handled = child.dispatchTouchEvent(event);            }

在看第 9条注释:
这个是发生在,子View可能正在处理事件(mFirstTouchEvent!=null),此时ViewGroup决定拦截事件,这是ViewGroup就会偷取子View的事件,向子View发送一个cancel事件,然后将子View从TouchTarget中移除,将导致mFirstTouchEvent重新为 null, 使得接下来的事件交由 ViewGroup自身处理
相关代码:

 final boolean cancelChild = resetCancelNextUpFlag(target.child)                                || intercepted;                                //传递一个 cancel事件给 子View                        if (dispatchTransformedTouchEvent(ev, cancelChild,                                target.child, target.pointerIdBits)) {                            handled = true;                        }                        if (cancelChild) {                            if (predecessor == null) {                                mFirstTouchTarget = next;                            } else {                                predecessor.next = next;                            }                            //回收target                            target.recycle();                            target = next;                            continue;

那么事件的分发到这里,就基本讲完了,其中很多细节目前还不是很懂,需要以后继续学习,下面我上传流程图:
android点击事件的分发过程_第1张图片

更多相关文章

  1. android软键盘弹出,会把原来的界面挤上去的问题 处理方法
  2. Android O: 触摸事件传递流程源码分析(上)
  3. Android学习方法路线
  4. Android横竖屏切换方法
  5. 在deepin系统中adb操作android文件的方法

随机推荐

  1. Android发送邮件到指定邮箱(可带附件)
  2. Android(安卓)Market 账号注册和应用发布
  3. android中做网络请求的几种方式
  4. android 登陆、提交数据或加载数据时提示
  5. Testing和Instrumentation
  6. Android(安卓)Support 包:Android(安卓)Su
  7. Android(安卓)布局管理器 之 RelativeLay
  8. Android(安卓)系统开发(2)--Android(安卓)T
  9. Android(安卓)推送通知指南
  10. SQL Server 日期和时间的内部存储过程