原创

android点击事件的分发过程

本文将讲述android点击事件的分发过程,我的上一篇文章讲述了Android点击事件的来源,本文接着讲述当点击事件传输到Activity之后 分发的过程是什么样的。
通过上一篇文章我们知道,事件最终会通过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<View> 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点击事件的分发过程

 

来自: http://blog.csdn.net/a992036795/article/details/51698023

 

正文到此结束
Loading...