android点击事件的分发过程

FlorrieDula 8年前
   <p>本文将讲述android点击事件的分发过程,我的上一篇文章讲述了<a href="http://www.open-open.com/lib/view/open1466152189045.html">Android点击事件的来源</a>,本文接着讲述当点击事件传输到Activity之后 分发的过程是什么样的。<br> 通过上一篇文章我们知道,事件最终会通过activity分发到PhoneWindow再到DecorView最后到他的子View。</p>    <p>那我们就从Activity的dispatchTouchEvent方法看起吧。<br> Activity#dispatchTouchEvent</p>    <pre>  <code class="language-java"> 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);      }</code></pre>    <p>从这段代码可以看出:activity把事件交给了 phoneWindow,向下传递。<br> 如果下层没有处理这个事件,那么activity将调用自己的onTouchEvent来处理这个事件。</p>    <p>我们接看PhoneWindow的superDispatchTouchEvent<br> PhoneWindow#superDispatchTouchEvent</p>    <pre>  <code class="language-java">@Override      public boolean superDispatchTouchEvent(MotionEvent event) {          //传递给DecorView          return mDecor.superDispatchTouchEvent(event);      }</code></pre>    <p>它将事件传递给了DecorView。<br> DecorView的superDispatchTouchEvent</p>    <pre>  <code class="language-java">public boolean superDispatchTouchEvent(MotionEvent event) {              return super.dispatchTouchEvent(event);          }  </code></pre>    <p>它这里调用了super.dispatchTouchEvent(event),实际就是调用了ViewGoup的<br> dispatchTouchEvent方法。<br> ViewGroup#dispatchTouchEvent</p>    <pre>  <code class="language-java"> @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;      }</code></pre>    <p>这个方法代码比较长,内容也比较多。我们先看第一个注释的代码:</p>    <pre>  <code class="language-java">  private void resetTouchState() {          clearTouchTargets();          resetCancelNextUpFlag(this);          //清楚标志位          mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;          mNestedScrollAxes = SCROLL_AXIS_NONE;      }  </code></pre>    <p>看以看到如果是ACTION_DOWN事件的话,它会清楚FLAG_DISALLOW_INTERCEPT这个标志</p>    <p>接着看第二个注释的地方:</p>    <pre>  <code class="language-java">  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;              }</code></pre>    <p>这块代码,注释中已经说得差不多了。2条结论:<br> 1、如果ViewGroup决定拦截事件,那么将不会再进入这个分支判断,后续的事件将都交由它处理。<br> 2、可以使用 requestDisallowInterceptTouchEvent,使得ViewGroup不拦截事件,但为ACTION_DOWN事件无效。</p>    <p>接着会遍历所有的子View 并调用dispatchTransformedTouchEvent进行事件分发,我们来看这个方法的代码:</p>    <pre>  <code class="language-java">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;      }  </code></pre>    <p>这个方法,将会进行事件分发,如果传入的child不为null,则会传递给子View,调用子View的dispatchTouchEvent。</p>    <p>如果child为null 则会掉用 super.dispatchTouchEvent。当然ViewGroup的 父类是View 所以会执行view 的dispatchTouchEvent,这时就会调用ViewGroup的onTouchEvent了。</p>    <p>View#dispatchTouchEvent</p>    <pre>  <code class="language-java"> 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;      }</code></pre>    <p>这样事件就分发到子View的onTouchEvent或者 自身的onTouchEvent了。</p>    <p>我们回过头,在看一下ViewGroup#dispatchTouchEvent这个方法中,注释7~9,看看它是怎么传递事件到自身 onTouchEvent的,和如果给mFirstTouchEvent赋值的。</p>    <p>我们先来看注释7、addTouchTarget方法是怎么赋值的。</p>    <pre>  <code class="language-java">   private TouchTarget addTouchTarget(View child, int pointerIdBits) {          TouchTarget target = TouchTarget.obtain(child, pointerIdBits);          target.next = mFirstTouchTarget;          mFirstTouchTarget = target;          return target;      }</code></pre>    <p>在看第8条注释,如果mFirstTouchEvent==null 说明没有子View处理事件,这时将向上冒泡,那我们来看看它怎么处理的。</p>    <pre>  <code class="language-java"> // 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);</code></pre>    <p>这是将调用dispatchTransformedTouchEvent 并传入的child==null,那么将会执行<br> dispatchTransformedTouchEvent 方法中的<br> handled = super.dispatchTouchEvent(event);<br> 这个上文已经分析过了。</p>    <pre>  <code class="language-java"> if (child == null) {                  handled = super.dispatchTouchEvent(event);              } else {                  handled = child.dispatchTouchEvent(event);              }</code></pre>    <p>在看第 9条注释:<br> 这个是发生在,子View可能正在处理事件(mFirstTouchEvent!=null),此时ViewGroup决定拦截事件,这是ViewGroup就会偷取子View的事件,向子View发送一个cancel事件,然后将子View从TouchTarget中移除,将导致mFirstTouchEvent重新为 null, 使得接下来的事件交由 ViewGroup自身处理<br> 相关代码:</p>    <pre>  <code class="language-java"> 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;</code></pre>    <p>那么事件的分发到这里,就基本讲完了,其中很多细节目前还不是很懂,需要以后继续学习,下面我上传流程图:<br> <img alt="这里写图片描述" src="https://simg.open-open.com/show/08823f48789e689f1743781947921504.jpg"></p>    <p> </p>    <p>来自: <a href="/misc/goto?guid=4959674642526883619" rel="nofollow">http://blog.csdn.net/a992036795/article/details/51698023</a></p>    <p> </p>