Android ViewGroup 触摸屏事件派发机制和源码分析

Android ViewGroup 触摸屏事件派发机制和源码分析

Android 中不管是View 还是ViewGoup,触摸事件来的时候都是从dispatchTouchEvent开始的.其中 dispatchTouchEvent()是View.java 的方法,ViewGroup 只是重写了这个方法.
ViewGroup的 dispatchTouchEvent() 之前最好先看View的dispatchTouchEvent().View 的逻辑简单些,而且 ViewGroup的 dispatchTouchEvent()最终也会调用View的dispatchTouchEvent().
以下分析基于Android L.
我们现在从ViewGroup的 dispatchTouchEvent()开始分析.相关代码如下:
   
   
  1. @Override
  2. public boolean dispatchTouchEvent(MotionEvent ev) {
  3. if (mInputEventConsistencyVerifier != null) {
  4. mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
  5. }
  6. // If the event targets the accessibility focused view and this is it, start
  7. // normal event dispatch. Maybe a descendant is what will handle the click.
  8. if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
  9. ev.setTargetAccessibilityFocus(false);
  10. }
  11. if (DBG_MOTION || DBG_TOUCH) {
  12. Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent 1: ev = " + ev + ",mFirstTouchTarget = "
  13. + mFirstTouchTarget + ",this = " + this);
  14. }
  15. boolean handled = false;
  16. if (onFilterTouchEventForSecurity(ev)) {//调用View.java判断当前View 没有被遮蔽
  17. final int action = ev.getAction();
  18. final int actionMasked = action & MotionEvent.ACTION_MASK;
  19. // Handle an initial down.
  20. if (actionMasked == MotionEvent.ACTION_DOWN) {
  21. // Throw away all previous state when starting a new touch gesture.
  22. // The framework may have dropped the up or cancel event for the previous gesture
  23. // due to an app switch, ANR, or some other state change.
  24. cancelAndClearTouchTargets(ev);
  25. resetTouchState();//mFirstTouchTarget设置为null;mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
  26. }
  27. // Check for interception.
  28. final boolean intercepted;
  29. if (actionMasked == MotionEvent.ACTION_DOWN
  30. || mFirstTouchTarget != null) {
  31. final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//true:不拦截;false:拦截
  32. if (!disallowIntercept) {//拦截
  33. intercepted = onInterceptTouchEvent(ev);// 默认返回false
  34. /// M : add log to help debugging
  35. if (intercepted == true && DBG_TOUCH) {
  36. Xlog.d(TAG, "Touch event was intercepted event = " + ev
  37. + ",this = " + this);
  38. }
  39. ev.setAction(action); // restore action in case it was changed
  40. } else {//不拦截
  41. intercepted = false;
  42. }
  43. } else {
  44. // There are no touch targets and this action is not an initial down
  45. // so this view group continues to intercept touches.
  46. intercepted = true;
  47. }
  48. // If intercepted, start normal event dispatch. Also if there is already
  49. // a view that is handling the gesture, do normal event dispatch.
  50. if (intercepted || mFirstTouchTarget != null) {
  51. ev.setTargetAccessibilityFocus(false);
  52. }
  53. // Check for cancelation.
  54. final boolean canceled = resetCancelNextUpFlag(this)
  55. || actionMasked == MotionEvent.ACTION_CANCEL;//是否cancle事件
  56. // Update list of touch targets for pointer down, if needed.
  57. final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;//默认true,用于判断分发事件,作用是判断可以把事件分发到多个子View.
  58. if (DBG_MOTION) {
  59. Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent 2: actionMasked = " + actionMasked
  60. + ",intercepted = " + intercepted + ",canceled = " + canceled + ",split = "
  61. + split + ",mChildrenCount = " + mChildrenCount + ",mFirstTouchTarget = "
  62. + mFirstTouchTarget + ",this = " + this);
  63. }
  64. TouchTarget newTouchTarget = null;
  65. boolean alreadyDispatchedToNewTouchTarget = false;
  66. if (!canceled && !intercepted) {//没有被拦截
  67. // If the event is targeting accessiiblity focus we give it to the
  68. // view that has accessibility focus and if it does not handle it
  69. // we clear the flag and dispatch the event to all children as usual.
  70. // We are looking up the accessibility focused host to avoid keeping
  71. // state since these events are very rare.
  72. View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
  73. ? findChildWithAccessibilityFocus() : null;
  74. if (actionMasked == MotionEvent.ACTION_DOWN
  75. || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
  76. || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
  77. final int actionIndex = ev.getActionIndex(); // always 0 for down
  78. final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
  79. : TouchTarget.ALL_POINTER_IDS;
  80. // Clean up earlier touch targets for this pointer id in case they
  81. // have become out of sync.
  82. removePointersFromTouchTargets(idBitsToAssign);
  83. final int childrenCount = mChildrenCount;
  84. if (newTouchTarget == null && childrenCount != 0) {
  85. final float x = ev.getX(actionIndex);
  86. final float y = ev.getY(actionIndex);
  87. // Find a child that can receive the event.
  88. // Scan children from front to back.
  89. final ArrayList<View> preorderedList = buildOrderedChildList();
  90. final boolean customOrder = preorderedList == null
  91. && isChildrenDrawingOrderEnabled();
  92. final View[] children = mChildren;
  93. for (int i = childrenCount - 1; i >= 0; i--) {
  94. final int childIndex = customOrder
  95. ? getChildDrawingOrder(childrenCount, i) : i;
  96. final View child = (preorderedList == null)
  97. ? children[childIndex] : preorderedList.get(childIndex);
  98. // If there is a view that has accessibility focus we want it
  99. // to get the event first and if not handled we will perform a
  100. // normal dispatch. We may do a double iteration but this is
  101. // safer given the timeframe.
  102. if (childWithAccessibilityFocus != null) {
  103. if (childWithAccessibilityFocus != child) {
  104. continue;
  105. }
  106. childWithAccessibilityFocus = null;
  107. i = childrenCount - 1;
  108. }
  109. if (!canViewReceivePointerEvents(child)
  110. || !isTransformedTouchPointInView(x, y, child, null)) {//! View.VISIBLE || ! is in view
  111. ev.setTargetAccessibilityFocus(false);
  112. if (DBG_MOTION) {
  113. Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent continue 6: i = "
  114. + i + ",count = " + childrenCount + ",child = " + child
  115. + ",this = " + this);
  116. }
  117. continue;
  118. }
  119. newTouchTarget = getTouchTarget(child);//Down事件:由于之前的mFirstTouchTarget已经被设置为null,这里newTouchTarget 赋值结果也是null
  120. if (DBG_MOTION) {
  121. Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent to child 3: child = "
  122. + child + ",childrenCount = " + childrenCount + ",i = " + i
  123. + ",newTouchTarget = " + newTouchTarget
  124. + ",idBitsToAssign = " + idBitsToAssign
  125. + ",mFirstTouchTarget = " + mFirstTouchTarget
  126. + ",this = " + this);
  127. }
  128. if (newTouchTarget != null) {
  129. // Child is already receiving touch within its bounds.
  130. // Give it the new pointer in addition to the ones it is handling.
  131. newTouchTarget.pointerIdBits |= idBitsToAssign;
  132. break;
  133. }
  134. resetCancelNextUpFlag(child);
  135. if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {//将事件传递给child view
  136. // Child wants to receive touch within its bounds.
  137. mLastTouchDownTime = ev.getDownTime();
  138. if (preorderedList != null) {
  139. // childIndex points into presorted list, find original index
  140. for (int j = 0; j < childrenCount; j++) {
  141. if (children[childIndex] == mChildren[j]) {
  142. mLastTouchDownIndex = j;
  143. break;
  144. }
  145. }
  146. } else {
  147. mLastTouchDownIndex = childIndex;
  148. }
  149. mLastTouchDownX = ev.getX();
  150. mLastTouchDownY = ev.getY();
  151. newTouchTarget = addTouchTarget(child, idBitsToAssign);
  152. alreadyDispatchedToNewTouchTarget = true;
  153. break;
  154. }
  155. // The accessibility focus didn't handle the event, so clear
  156. // the flag and do a normal dispatch to all children.
  157. ev.setTargetAccessibilityFocus(false);
  158. }
  159. if (preorderedList != null) preorderedList.clear();
  160. }
  161. if (newTouchTarget == null && mFirstTouchTarget != null) {
  162. // Did not find a child to receive the event.
  163. // Assign the pointer to the least recently added target.
  164. newTouchTarget = mFirstTouchTarget;
  165. while (newTouchTarget.next != null) {
  166. newTouchTarget = newTouchTarget.next;
  167. }
  168. newTouchTarget.pointerIdBits |= idBitsToAssign;
  169. }
  170. }
  171. }
  172. // Dispatch to touch targets.
  173. if (mFirstTouchTarget == null) {//事件没有被消耗掉或者被拦截了
  174. if (DBG_MOTION) {
  175. Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent mFirstTouchTarget = null,"
  176. + " canceled = " + canceled + ",this = " + this);
  177. }
  178. // No touch targets so treat this as an ordinary view.
  179. handled = dispatchTransformedTouchEvent(ev, canceled, null,
  180. TouchTarget.ALL_POINTER_IDS);
  181.                 //内部会转调super.dispatchTouchEvent也就是View的onTouchEvent(),
  182.                 //可以理解为:如果如果视图的child 都没有消耗掉这个事件,那么视图自己来调用onTouchEvent.
  183.                 //还有一种情况是被拦截了,也是会调用自己的onTouchEvent
  184. } else {
  185. // Dispatch to touch targets, excluding the new touch target if we already
  186. // dispatched to it. Cancel touch targets if necessary.
  187. TouchTarget predecessor = null;
  188. TouchTarget target = mFirstTouchTarget;
  189. while (target != null) {
  190. final TouchTarget next = target.next;
  191. if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {//ACTION_DWON执行这里
  192. handled = true;
  193. } else {//其他事件move/up执行这里
  194. final boolean cancelChild = resetCancelNextUpFlag(target.child)
  195. || intercepted;
  196. if (dispatchTransformedTouchEvent(ev, cancelChild,
  197. target.child, target.pointerIdBits)) {
  198. handled = true;
  199. }
  200. if (DBG_MOTION) {
  201. Xlog.d(TAG, "dispatchTouchEvent middle 5: cancelChild = " + cancelChild
  202. + ",mFirstTouchTarget = " + mFirstTouchTarget + ",target = "
  203. + target + ",predecessor = " + predecessor + ",next = " + next
  204. + ",this = " + this);
  205. }
  206. if (cancelChild) {//child 通知父元素拦截后续事件
  207. if (predecessor == null) {
  208. mFirstTouchTarget = next;//此时next=null,所以mFirstTouchTarget=null,后续再由其他事件来就是执行的上面的if分支而不是现在的else分支
  209. } else {
  210. predecessor.next = next;
  211. }
  212. target.recycle();
  213. target = next;
  214. continue;
  215. }
  216. }
  217. predecessor = target;
  218. target = next;
  219. }
  220. }
  221. // Update list of touch targets for pointer up or cancel, if needed.
  222. if (canceled
  223. || actionMasked == MotionEvent.ACTION_UP
  224. || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
  225. resetTouchState();
  226. } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
  227. final int actionIndex = ev.getActionIndex();
  228. final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
  229. removePointersFromTouchTargets(idBitsToRemove);
  230. }
  231. }
  232. if (DBG_MOTION) {
  233. Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent end 4: handled = " + handled
  234. + ",mFirstTouchTarget = " + mFirstTouchTarget + ",this = " + this);
  235. }
  236. if (!handled && mInputEventConsistencyVerifier != null) {
  237. mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
  238. }
  239. return handled;
  240. }
上面这个方法有点长,我们先从ACTION_DWON事件开始分析.
ACTION_DWON 事件处理之前,是一些关于手手势操作的处理,不是我们目前要关注的重点,接这再调用View.java判断当前View 没有被遮蔽,这些和View.java的dispatchTouchEvent()相似.
从24行就是ACTION_DWON的处理逻辑的开始.
其中cancelAndClearTouchTargets()主要是执行一个ACTION_CANCEL和设置mFirstTouchTarget为null.看注释就可以知道,执行 ACTION_CANCEL是为了防止ANR和其他原因导致上一个触摸动作的 ACTION_CANCEL或者ACTION_UP被丢失了.
resetTouchState():mFirstTouchTarget设置为null同时执行mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT.
总之 ACTION_DWON   来了之后 mFirstTouchTarget会被设置为null,而且 mGroupFlags  已经被设置了不容许拦截的标志.
接着看3 4-48行代码,为了方便将这一小段重新贴出来.
   
   
  1. //......................
  2. if (actionMasked == MotionEvent.ACTION_DOWN
  3. || mFirstTouchTarget != null) {//action=down的时候就可以通过了
  4. final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//true:不拦截;false:拦截
  5. if (!disallowIntercept) {
  6. intercepted = onInterceptTouchEvent(ev);// 默认返回false
  7. ev.setAction(action); // restore action in case it was changed
  8. } else {
  9. intercepted = false;
  10. }
  11. }//....................
由于之前执行了 cancelAndClearTouchTargets()和 resetTouchState(), mGroupFlags & FLAG_DISALLOW_INTERCEPT就等于0,所以booelan disallowIntercept现在就是false,也就是说接下来会执行onInterceptTouchEvent().这个方法在ViewGroup里面默认是直接返回false的.所以变量intercepted的值由 onInterceptTouchEvent()的返回值来决定.
上面又说到 mGroupFlags   设置是否容许拦截标志位的问题,我们在源码中搜索会发现有 requestDisallowInterceptTouchEvent() ,这个方法是公开用来调用,决定后续事件来的时候父层的view是否可以调用它的 onInterceptTouchEvent().
对于底层的View来说,有一种方法可以阻止父层的View截获touch事件,就是调用getParent().requestDisallowInterceptTouchEvent(true);方法。一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action.

61行是判断是否ACTION_CANCLE事件的,并保持到boolean canceled变量中.

65行是判断是否可以分发事件,并保存到split变量中,作用是判断可以把事件分发到多个子View. 这个同样在ViewGroup中提供了public的方法: public void setMotionEventSplittingEnabled ( boolean split)来设置.

75行if (!canceled && !intercepted) {,就是开始事件的分发处理.ACTION_DOWN事件执行到这里canceled = false,intercepted的值由 onInterceptTouchEvent ( ev ) 来决定.

85行 if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE)
这个条件满足了才会将touch事件传递到子View,当然也要child view存在也就是mChildrenCount >0. 也就是133行的if (newTouchTarget == null && childrenCount != 0)是否可以通过,由于 newTouchTarget   在ACTION_DOWN时是null,所以这个条件pass.接着会调用buildOrderedChildList()方法得到一个子View 的ArrayList 集合,然后从childrenCount - 1 到0开始循环查找那个View是View.VISIBIE状态或者在View视图的范围之内,然后调用dispatchTransformedTouchEvent来让child view处理事件.

135行调用getTouchTarget(child)来赋值newTouchTarget.当是Down事件时:由于之前的mFirstTouchTarget已经被设置为null,这里newTouchTarget 赋值结果也是null,所以181行的break跳出for循环暂时不会执行.

152行开始事件传递到child View,if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign))这个if判断的结果由 dispatchTransformedTouchEvent()来决定,这个方法代码很长就不贴出来,但是流程不是很复杂.
dispatchTransformedTouchEvent 在这里它最终会调用child.dispatchTouchEvent(),并且将child.dispatchTouchEvent()的返回值作为 dispatchTransformedTouchEvent()的返回值,也就是if()判断的条件值.显然 dispatchTransformedTouchEvent就是递归调用 dispatchTouchEvent()方法,如果递归传递到了一个View哪里,一般都会执行onTouchEvent(),并且 onTouchEvent()的返回值就是 dispatchTouchEvent()的返回值.(为什么是一般会执行需要查看View.java)
在这个if()通过之后会做如下3件事情:
a.调用addTouchTarget()来赋值newTouchTarget,同时 mFirstTouchTarget也会被赋值,也就是说 mFirstTouchTarget和 newTouchTarget都不会是null了.
b.设置alreadyDispatchedToNewTouchTarget= true,这个变量和他的名字一样就是说已经将touch时间传递给新的目标view处理了,也就是消耗掉了.
c.跳出上面的for循环,因为一个touch事件只能被一个目标View消耗掉,也就是一个View的 dispatchTouchEvent()返回true.

以上代码的分析其实都还是只有ACTION_DOWN才能执行的,后面192-247就是一个if-else,而且具体怎么走是有 mFirstTouchTarget== null来判断.而 mFirstTouchTarget 由前面调用 dispatchTransformedTouchEvent ()返回true再来调用 addTouchTarget()来设置值的.而前面的 dispatchTransformedTouchEvent()返回值就是有递归调用的子View 的 dispatchTouchEvent()返回boolean 参数来决定.  dispatchTouchEvent()返回值一般和onTouchEvent()是一致的,也是由他决定的. 所以 dispatchTransformedTouchEvent()的返回值由 onTouchEvent()决定.这也决定了后面其他move和up事件来的时候192-247行里面的具体怎么执行.

   
   
  1. private TouchTarget addTouchTarget(View child, int pointerIdBits) {
  2. TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
  3. target.next = mFirstTouchTarget;
  4. mFirstTouchTarget = target;
  5. return target;
  6. }
注意上面提到的 addTouchTarget()在第一次执行的时候 mFirstTouchTarget 还是null,所以target.next 也会是null,但是target.child不会是null.这个在子视图调用getParent().requestDisallowInterceptTouchEvent(false)来让父元素拦截后续事件的时候很重要.

192-247行:
如果 mFirstTouchTarget ==null,说明ACTION_DOWN没有被消耗掉或者被拦截掉了.接着执行 dispatchTransformedTouchEvent(),这个方法的第3个参数是null,最终结果就是在方法内部执行的时候是会调用 super.dispatchTouchEvent(),也就是说不会继续传递给子View了.其中 super.dispatchTouchEvent()也就是View的 dispatchTouchEvent(),这个方法内部会执行onTouchEvent()方法.(可以参考View的触摸屏事件派发机制和分析.)
如果 mFirstTouchTarget !=null,执行后面的while循环.

206行:
这个while循环内部有一个if-else来处理,
其中 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) 只有Down事件才会执行这里,因为 alreadyDispatchedToNewTouchTarget  在这个方法的内部开头是false,只有在169行才能设置为true,169行只有是Down相关的事件才能走的流程,168行的addTouchTarget会使   target == newTouchTarget为true.这个分支就是表示事件已经被子View消耗掉了,ViewGoup自己接下来什么都没有了,直接return true,等后面的move/up事件
而else分支就是其他事件move/up才会走的流程.其实也是执行 dispatchTransformedTouchEvent()方法,只是参数不一样.

上面反复提到 dispatchTransformedTouchEvent(),这里单独分析一下.
   
   
  1. private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
  2. View child, int desiredPointerIdBits) {
  3. final boolean handled;
  4. // Canceling motions is a special case. We don't need to perform any transformations
  5. // or filtering. The important part is the action, not the contents.
  6. final int oldAction = event.getAction();
  7. if (DBG_MOTION) {
  8. Xlog.d(TAG, "dispatchTransformedTouchEvent 1: event = " + event + ",cancel = "
  9. + cancel + ",oldAction = " + oldAction + ",desiredPointerIdBits = "
  10. + desiredPointerIdBits + ",mFirstTouchTarget = " + mFirstTouchTarget
  11. + ",child = " + child + ",this = " + this);
  12. }
  13. if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
  14. event.setAction(MotionEvent.ACTION_CANCEL);
  15. if (child == null) {
  16. handled = super.dispatchTouchEvent(event);
  17. } else {
  18. handled = child.dispatchTouchEvent(event);
  19. }
  20. event.setAction(oldAction);
  21. if (DBG_MOTION) {
  22. Xlog.d(TAG, "Dispatch cancel action end: handled = " + handled + ",oldAction = "
  23. + oldAction + ",child = " + child + ",this = " + this);
  24. }
  25. return handled;
  26. }
  27. // Calculate the number of pointers to deliver.
  28. final int oldPointerIdBits = event.getPointerIdBits();
  29. final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
  30. // If for some reason we ended up in an inconsistent state where it looks like we
  31. // might produce a motion event with no pointers in it, then drop the event.
  32. if (newPointerIdBits == 0) {
  33. Xlog.i(TAG, "Dispatch transformed touch event without pointers in " + this);
  34. return false;
  35. }
  36. // If the number of pointers is the same and we don't need to perform any fancy
  37. // irreversible transformations, then we can reuse the motion event for this
  38. // dispatch as long as we are careful to revert any changes we make.
  39. // Otherwise we need to make a copy.
  40. final MotionEvent transformedEvent;
  41. if (newPointerIdBits == oldPointerIdBits) {
  42. if (child == null || child.hasIdentityMatrix()) {
  43. if (child == null) {
  44. handled = super.dispatchTouchEvent(event);
  45. } else {
  46. final float offsetX = mScrollX - child.mLeft;
  47. final float offsetY = mScrollY - child.mTop;
  48. event.offsetLocation(offsetX, offsetY);
  49. handled = child.dispatchTouchEvent(event);
  50. event.offsetLocation(-offsetX, -offsetY);
  51. }
  52. if (DBG_MOTION) {
  53. Xlog.d(TAG, "dispatchTransformedTouchEvent 2 to child " + child
  54. + ",handled = " + handled + ",mScrollX = " + mScrollX + ",mScrollY = "
  55. + mScrollY + ",mFirstTouchTarget = " + mFirstTouchTarget + ",event = "
  56. + event + ",this = " + this);
  57. }
  58. return handled;
  59. }
  60. transformedEvent = MotionEvent.obtain(event);
  61. } else {
  62. transformedEvent = event.split(newPointerIdBits);
  63. }
  64. // Perform any necessary transformations and dispatch.
  65. if (child == null) {
  66. handled = super.dispatchTouchEvent(transformedEvent);
  67. } else {
  68. final float offsetX = mScrollX - child.mLeft;
  69. final float offsetY = mScrollY - child.mTop;
  70. transformedEvent.offsetLocation(offsetX, offsetY);
  71. if (! child.hasIdentityMatrix()) {
  72. transformedEvent.transform(child.getInverseMatrix());
  73. }
  74. handled = child.dispatchTouchEvent(transformedEvent);
  75. }
  76. if (DBG_MOTION) {
  77. Xlog.d(TAG, "dispatchTransformedTouchEvent 3 to child " + child + ",handled = "
  78. + handled + ",mScrollX = " + mScrollX + ",mScrollY = " + mScrollY
  79. + ",mFirstTouchTarget = " + mFirstTouchTarget + ",transformedEvent = "
  80. + transformedEvent + ",this = " + this);
  81. }
  82. // Done.
  83. transformedEvent.recycle();
  84. return handled;
  85. }
其实逻辑很简单,如果第2个参数cancel为true,就是表示这个事件是ACTION_CANCEL,起码意见被当成是 ACTION_CANCEL,接着判断第3个参数child是否为null,child为null,就调用父类的dispatchTouchEvent,也就是执行super.  dispatchTouchEvent().如果 3个参数child不是为null,就调用child.  dispatchTouchEvent(),并且这个 dispatchTouchEvent()的返回值就是dispatchTransformedTouchEvent()的返回值.如果第2个参数cancel不是null,还是会一样判断 3个参数child是否为null,后面的流程都是和前面一样.

到这里,ViewGroup 触摸屏事件派发流程就算是结束了.
总结一下:
1.触摸屏事件是先传递到最顶层的ViewGroup(也就是最parent)的dispatchTouchEvent()开始处理的,再传递到子View(child view)的dispatchTouchEvent().

2.如果ViewGroup的onInterceptTouchEvent()拦截了事件也就是返回true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理.不会再传递给子View.
如果ViewGroup的onInterceptTouchEvent()返回false,就是不拦截,后续的move,up和down事件一样传递给最终的目标View的onTouchEvent()来处理.

3.如果子View将事件消耗掉了,ViewGroup自己就不能处理了,只能等后续的move/up事件了.

4.某个view 一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么同一个事件序列中的其他事件就不会再交给他来处理,并将事件提交给父元素去处理(关键是mFirstTouchTarget).这里可以理解为如果ViewGroup的child 都没有消耗掉事件就是执行ViewGroup 的super()也就是View来执行.

5.如果View不消耗除ACTION_Down以外的其他事件(up/move),那么这个点击事件就会消失,此时父元素的onTouchEvent不会执行,并且当前view可以继续接受后续事件,最终这些消失的点击事件会传递给activity处理.

6.子View想要禁止父View的拦截事件只要调用getParent().requestDisallowInterceptTouchEvent(true)方法.一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action.





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值