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.





更多相关文章

  1. android实现字体闪烁动画的方法
  2. Android(安卓)Wifi模块分析(三)
  3. Android中dispatchDraw分析
  4. Android四大基本组件介绍与生命周期
  5. 在Fragment中设置控件点击方法,执行失败。
  6. Android(安卓)Service AIDL
  7. Android调用天气预报的WebService简单例子
  8. android打电话发短信
  9. android 拨打紧急号码,通话时开启免提功能实现

随机推荐

  1. Android获取基站坐标代码
  2. Android(安卓)ArrayAdapter的使用
  3. Android使用HttpURLConnection获取数据
  4. android 之用Hello World做项目结构分析
  5. android获取imei和imsi
  6. Android实现登录对话框
  7. android:inputType的XML与Java代码对应关
  8. [Android] RatingBar详解
  9. android录音和得到音量
  10. android解决UI阻塞问题——创建AsyncTask