TouchEvent分发过程一:TouchEvent在ViewGroup中的分发过程

最近在做一个左右翻页的容器控件,每页显示一个不能获取焦点的控件时一切正常,结果在添加了一个ListView之后不能翻到其他页了,研究后发现这与触摸事件的传递有关系。
Android触摸事件分发的流程可以学习http://blog.csdn.net/stonecao/article/details/6759189,非常详细。
首先分析一下TouchEvent传递到Activity之后的分发流程:
1、PhoneWindow.DecorView.dispatchTouchEvent:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
   final Callback cb = getCallback();
   return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
           : super.dispatchTouchEvent(ev);
}
这里的Callback对象cb就是我们的Activity,TouchEvent就是通过cb.dispatchTouchEvent分发到Activity中的。
2、Activity.dispatchTouchEvent:
public boolean dispatchTouchEvent(MotionEvent ev) {
      if (ev.getAction() == MotionEvent.ACTION_DOWN) {
          onUserInteraction();
      }
      if (getWindow().superDispatchTouchEvent(ev)) {
          return true;
      }
      return onTouchEvent(ev);
  }
这里onUserInteraction()只用于监控用户是否与Activity发生交互,对TouchEvent分发无影响,分发流程进入PhoneWindow。
3、PhoneWindow.superDispatchTouchEvent:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
      return mDecor.superDispatchTouchEvent(event);
}
public boolean superDispatchTouchEvent(MotionEvent event) {
      return super.dispatchTouchEvent(event);
}
TouchEvent最终派发给了DecorView的父类,其父类是FrameLayout,FrameLayout并没有覆盖disPatchTouchEvent,因此继续找FrameLayout的父类,也就是ViewGroup类。
4、ViewGroup.dispatchTouchEvent:
  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
  		// 检测TouchEvent的完整性,是否配对
      if (mInputEventConsistencyVerifier != null) {
          mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
      }
      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.
              //如果是ACTION_DOWN,清除TouchTarget,发送ACTION_CANCEL,结束上一个触摸循环
              cancelAndClearTouchTargets(ev);
              resetTouchState();
          }
          // Check for interception.
          final boolean intercepted;
          if (actionMasked == MotionEvent.ACTION_DOWN
                  || mFirstTouchTarget != null) {
              final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
              if (!disallowIntercept) {//判断子视图有没有强制父视图不能截断TouchEvent
                  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;
          }
          // Check for cancelation.
          final boolean canceled = resetCancelNextUpFlag(this)
                  || actionMasked == MotionEvent.ACTION_CANCEL;
          // Update list of touch targets for pointer down, if needed.
          //如果在layout中没有设置,Android3.0以上返回true
          final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
          TouchTarget newTouchTarget = null;
          boolean alreadyDispatchedToNewTouchTarget = false;
          if (!canceled && !intercepted) {//如果不是取消键,并且父视图不截断,进入以下处理
              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 (childrenCount != 0) {
                      // Find a child that can receive the event.
                      // Scan children from front to back.
                      final View[] children = mChildren;
                      final float x = ev.getX(actionIndex);
                      final float y = ev.getY(actionIndex);
                      for (int i = childrenCount - 1; i >= 0; i--) {
                          final View child = children[i];
                          //判断子视图能不能接受TouchEvent,TouchEvent是否在子视图范围内
                          //如果不是则跳过该子视图
                          if (!canViewReceivePointerEvents(child)
                                  || !isTransformedTouchPointInView(x, y, child, null)) {
                              continue;
                          }
                          //获取该视图的TouchTarget,如果该视图已经在TouchTarget列表中,则表明该子视图正在处理这一次的TouchEvent
                          //则不需要再查询别的视图处理该事件
                          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);
                          //交由子视图处理TouchTarget,如果子视图消耗掉该事件,则将子视图加入TouchTarget列表中
                          if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                              // Child wants to receive touch within its bounds.
                              mLastTouchDownTime = ev.getDownTime();
                              mLastTouchDownIndex = i;
                              mLastTouchDownX = ev.getX();
                              mLastTouchDownY = ev.getY();
                              newTouchTarget = addTouchTarget(child, idBitsToAssign);
                              alreadyDispatchedToNewTouchTarget = true;
                              break;
                          }
                      }
                  }
									//没有子视图处理该事件
                  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;
                  }
              }
          }
          // 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);
          } 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;
              }
          }
          // 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;
  }
  从ACTION_DOWN情况开始分析:
  第一步:
  if (actionMasked == MotionEvent.ACTION_DOWN) {
        cancelAndClearTouchTargets(ev);
        resetTouchState();
  }
  如果是ACTION_DOWN,调用cancelAndClearTouchTargets(ev);清空TouchTarget链表。


  第二步:

  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;
  }
  如果此时为ACTION_DOWN,前面清空了TouchTargets列表,所以mFirstTouchEvent==null;
  根据onInterceptTouchEvent(ev);返回值不同第三步中包含两种情况的分析。


  第三步:
  第一种情况:onInterceptTouchEvent(ev);返回true。
  1、此时不会遍历子View分发TouchEvent。

 if (mFirstTouchTarget == null) {
      // No touch targets so treat this as an ordinary view.
      handled = dispatchTransformedTouchEvent(ev, canceled, null,
              TouchTarget.ALL_POINTER_IDS);
  } else {
  ...
  }
  2、进入dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
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) {
         //此时不进入,不分析
      }
      final int oldPointerIdBits = event.getPointerIdBits();
      final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
      if (newPointerIdBits == 0) {
          return false;
      }
      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);
      }
      return handled;
  }
  3、此时调用super.dispatchTouchEvent(event);
 public boolean dispatchTouchEvent(MotionEvent event) {
      if (mInputEventConsistencyVerifier != null) {
          mInputEventConsistencyVerifier.onTouchEvent(event, 0);
      }
      if (onFilterTouchEventForSecurity(event)) {
          //noinspection SimplifiableIfStatement
          ListenerInfo li = mListenerInfo;
          if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                  && li.mOnTouchListener.onTouch(this, event)) {
              return true;
          }
          if (onTouchEvent(event)) {
              return true;
          }
      }
      if (mInputEventConsistencyVerifier != null) {
          mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
      }
      return false;
  }
  View中首先查找有没有注册监听器,如果有注册,则优先交给监听器处理,如果没有,交给onTouchEvent(event)处理。
  4、如果后续又有一个ACTION_MOVE事件分发进来。
  回到第二步中的判断,此时mFirstTouchTarget==null,并且actionMasked != MotionEvent.ACTION_DOWN。直接进入else。
  所以所有ACTION_DOWN的后续事件都不会再判断onInterceptTouchEvent(ev);的返回值,而是跳过子View直接在当前ViewGroup的监听器或onTouchEvent中处理。
  也就是说在onInterceptTouchEvent(ev);的MotionEvent.ACTION_DOWN中返回true时,后续所有事件都不会交给子View处理。
  
  第二种情况:onInterceptTouchEvent(ev);返回false。
  1、回到第二步中的判断:
  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、此时接下来会遍历子View分发TouchEvent。
  if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
      // Child wants to receive touch within its bounds.
      mLastTouchDownTime = ev.getDownTime();
      mLastTouchDownIndex = i;
      mLastTouchDownX = ev.getX();
      mLastTouchDownY = ev.getY();
      newTouchTarget = addTouchTarget(child, idBitsToAssign);
      alreadyDispatchedToNewTouchTarget = true;
      break;
  }
  该部分代码完成遍历子View,如果有子View消耗了TouchEvent,则把该子View添加到TouchTarget中的过程。
  3、如果子View消耗了该TouchEvent,则mFirstTouchTarget!=null,进入else处理
  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 {
              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;
      }
  }
  此时alreadyDispatchedToNewTouchTarget为true,target == newTouchTarget,TouchTarget列表中只有一个TouchTarget,所以返回handled为true。
  4、如果此时下一个事件ACTION_MOVE传递进来,又可分为两种情况
  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;
  }
  情况一:onInterceptTouchEvent(ev);返回true。
  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 {
              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;
      }
  }
  执行else内的内容,final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;返回true。
  因此,后续的ACTION_MOVE等事件都不会再分发到该子View。
  情况二:onInterceptTouchEvent(ev);返回false。事件继续分发到子View。
  
  第四步:有一个ACTION_CANCEL或ACTION_UP分发进来的时候。
  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);
  }
  清除TouchTargets列表,回到最初的状态。
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值