标签:
本文传达的内容:
通过一个实例结合源码分析MotionEvent对象的传递过程。
实验:
自定义两个ViewGroup重写它们的dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()方法,和一个View重写它的dispatchTouchEvent(),onTouchEvent()方法。
MyGroupViewA:
1 public class MyGroupViewA extends LinearLayout { 2 3 public MyGroupViewA(Context context) { 4 super(context); 5 } 6 7 public MyGroupViewA(Context context, AttributeSet attrs) { 8 super(context, attrs); 9 } 10 11 public MyGroupViewA(Context context, AttributeSet attrs, 12 int defStyleAttr) { 13 super(context, attrs, defStyleAttr); 14 } 15 16 @Override 17 public boolean dispatchTouchEvent(MotionEvent ev) { 18 Log.d("dhn", "ViewGroupA dispatchTouchEvent" + ev.getAction()); 19 return super.dispatchTouchEvent(ev); 20 } 21 22 @Override 23 public boolean onInterceptTouchEvent(MotionEvent ev) { 24 Log.d("dhn", "ViewGroupA onInterceptTouchEvent" + ev.getAction()); 25 return super.onInterceptTouchEvent(ev); 26 } 27 28 @Override 29 public boolean onTouchEvent(MotionEvent event) { 30 Log.d("dhn", "ViewGroupA onTouchEvent" + event.getAction()); 31 return super.onTouchEvent(event); 32 } 33 }
MyGroupViewB:
1 public class MyGroupViewB extends LinearLayout { 2 3 public MyGroupViewB(Context context) { 4 super(context); 5 } 6 7 public MyGroupViewB(Context context, AttributeSet attrs) { 8 super(context, attrs); 9 } 10 11 public MyGroupViewB(Context context, AttributeSet attrs, 12 int defStyleAttr) { 13 super(context, attrs, defStyleAttr); 14 } 15 16 @Override 17 public boolean dispatchTouchEvent(MotionEvent ev) { 18 Log.d("dhn", "ViewGroupB dispatchTouchEvent" + ev.getAction()); 19 return super.dispatchTouchEvent(ev); 20 } 21 22 @Override 23 public boolean onInterceptTouchEvent(MotionEvent ev) { 24 Log.d("dhn", "ViewGroupB onInterceptTouchEvent" + ev.getAction()); 25 return super.onInterceptTouchEvent(ev); 26 } 27 28 @Override 29 public boolean onTouchEvent(MotionEvent event) { 30 Log.d("dhn", "ViewGroupB onTouchEvent" + event.getAction()); 31 return true; 32 } 33 }
MyView:
1 public class MyView extends View { 2 public MyView(Context context) { 3 super(context); 4 } 5 6 public MyView(Context context, AttributeSet attrs) { 7 super(context, attrs); 8 } 9 10 public MyView(Context context, AttributeSet attrs, int defStyleAttr) { 11 super(context, attrs, defStyleAttr); 12 } 13 14 @Override 15 public boolean onTouchEvent(MotionEvent event) { 16 Log.d("dhn", "View onTouchEnent" + event.getAction()); 17 return super.onTouchEvent(event); 18 } 19 20 @Override 21 public boolean dispatchTouchEvent(MotionEvent event) { 22 Log.d("dhn", "View dispatchTouchEvent" + event.getAction()); 23 return super.dispatchTouchEvent(event); 24 } 25 }
布局文件:activity_main.xml
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" 3 android:layout_height="match_parent"> 4 5 <com.dhn.touchevnettest.MyGroupViewA 6 android:layout_width="match_parent" 7 android:layout_height="match_parent" 8 android:background="@android:color/holo_blue_bright"> 9 10 <com.dhn.touchevnettest.MyGroupViewB 11 android:layout_width="300dp" 12 android:layout_height="300dp" 13 android:background="@android:color/holo_green_dark"> 14 15 <com.dhn.touchevnettest.MyView 16 android:id="@+id/myView" 17 android:layout_width="100dp" 18 android:layout_height="100dp" 20 android:background="@android:color/darker_gray"/> 21 22 23 </com.dhn.touchevnettest.MyGroupViewB> 24 25 </com.dhn.touchevnettest.MyGroupViewA> 26 27 </RelativeLayout>
效果图:
实验一:点击最小的方块
结果:
/com.dhn.touchevnettest D/dhn﹕ ViewGroupA dispatchTouchEvent0
/com.dhn.touchevnettest D/dhn﹕ ViewGroupA onInterceptTouchEvent0
/com.dhn.touchevnettest D/dhn﹕ ViewGroupB dispatchTouchEvent0
/com.dhn.touchevnettest D/dhn﹕ ViewGroupB onInterceptTouchEvent0
/com.dhn.touchevnettest D/dhn﹕ View dispatchTouchEvent0
/com.dhn.touchevnettest D/dhn﹕ View onTouchEnent0
/com.dhn.touchevnettest D/dhn﹕ ViewGroupB onTouchEvent0
/com.dhn.touchevnettest D/dhn﹕ ViewGroupA onTouchEvent0
分析:
首先事件从上层传递到MyGroupViewA对象,其dispatchTouchEvent()被调用,打印出ViewGroupA dispatchTouchEvent0。然后调用super.dispatchTouchEvent()即ViewGroup.dispatchTouchEvent(),该方法部分如下:
code1:
1 final boolean intercepted; 2 if (actionMasked == MotionEvent.ACTION_DOWN 3 || mFirstTouchTarget != null) { 4 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 5 if (!disallowIntercept) { 6 intercepted = onInterceptTouchEvent(ev); 7 ev.setAction(action); // restore action in case it was changed 8 } else { 9 intercepted = false; 10 } 11 } else { 12 // There are no touch targets and this action is not an initial down 13 // so this view group continues to intercept touches. 14 intercepted = true; 15 }
如果是ACTION_DOWN事件,或mFirstTouchTarget != null()则会调用onInterceptionTouchEvent(),这里显然会进入该方法,在该方法中,我们打印ViewGroupA onInterceptTouchEvent0,然后调用surper.onInterceptTouchEvent(),即ViewGroup.onInterceptTouchEvent(),我们来看下它的源码:
code2:
1 public boolean onInterceptTouchEvent(MotionEvent ev) { 2 return false; 3 }
该方法返回false,所以intercepted被赋值为false(code1第6行),接着往下执行ViewGroup.dispatchTouchEvent():
code3:
1 if (!canceled && !intercepted) { 2 3 // If the event is targeting accessiiblity focus we give it to the 4 // view that has accessibility focus and if it does not handle it 5 // we clear the flag and dispatch the event to all children as usual. 6 // We are looking up the accessibility focused host to avoid keeping 7 // state since these events are very rare. 8 View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() 9 ? findChildWithAccessibilityFocus() : null; 10 11 if (actionMasked == MotionEvent.ACTION_DOWN 12 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) 13 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { 14 final int actionIndex = ev.getActionIndex(); // always 0 for down 15 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) 16 : TouchTarget.ALL_POINTER_IDS; 17 18 // Clean up earlier touch targets for this pointer id in case they 19 // have become out of sync. 20 removePointersFromTouchTargets(idBitsToAssign); 21 22 final int childrenCount = mChildrenCount; 23 if (newTouchTarget == null && childrenCount != 0) { 24 final float x = ev.getX(actionIndex); 25 final float y = ev.getY(actionIndex); 26 // Find a child that can receive the event. 27 // Scan children from front to back. 28 final ArrayList<View> preorderedList = buildOrderedChildList(); 29 final boolean customOrder = preorderedList == null 30 && isChildrenDrawingOrderEnabled(); 31 final View[] children = mChildren; 32 for (int i = childrenCount - 1; i >= 0; i--) { 33 final int childIndex = customOrder 34 ? getChildDrawingOrder(childrenCount, i) : i; 35 final View child = (preorderedList == null) 36 ? children[childIndex] : preorderedList.get(childIndex); 37 38 // If there is a view that has accessibility focus we want it 39 // to get the event first and if not handled we will perform a 40 // normal dispatch. We may do a double iteration but this is 41 // safer given the timeframe. 42 if (childWithAccessibilityFocus != null) { 43 if (childWithAccessibilityFocus != child) { 44 continue; 45 } 46 childWithAccessibilityFocus = null; 47 i = childrenCount - 1; 48 } 49 50 if (!canViewReceivePointerEvents(child) 51 || !isTransformedTouchPointInView(x, y, child, null)) { 52 ev.setTargetAccessibilityFocus(false); 53 continue; 54 } 55 56 newTouchTarget = getTouchTarget(child); 57 if (newTouchTarget != null) { 58 // Child is already receiving touch within its bounds. 59 // Give it the new pointer in addition to the ones it is handling. 60 newTouchTarget.pointerIdBits |= idBitsToAssign; 61 break; 62 } 63 64 resetCancelNextUpFlag(child); 65 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { 66 // Child wants to receive touch within its bounds. 67 mLastTouchDownTime = ev.getDownTime(); 68 if (preorderedList != null) { 69 // childIndex points into presorted list, find original index 70 for (int j = 0; j < childrenCount; j++) { 71 if (children[childIndex] == mChildren[j]) { 72 mLastTouchDownIndex = j; 73 break; 74 } 75 } 76 } else { 77 mLastTouchDownIndex = childIndex; 78 } 79 mLastTouchDownX = ev.getX(); 80 mLastTouchDownY = ev.getY(); 81 newTouchTarget = addTouchTarget(child, idBitsToAssign); 82 alreadyDispatchedToNewTouchTarget = true; 83 break; 84 } 85 86 // The accessibility focus didn‘t handle the event, so clear 87 // the flag and do a normal dispatch to all children. 88 ev.setTargetAccessibilityFocus(false); 89 } 90 if (preorderedList != null) preorderedList.clear(); 91 } 92 93 if (newTouchTarget == null && mFirstTouchTarget != null) { 94 // Did not find a child to receive the event. 95 // Assign the pointer to the least recently added target. 96 newTouchTarget = mFirstTouchTarget; 97 while (newTouchTarget.next != null) { 98 newTouchTarget = newTouchTarget.next; 99 } 100 newTouchTarget.pointerIdBits |= idBitsToAssign; 101 } 102 } 103 }
遍历每个子元素,65行调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)),我们看下这个方法:
code4:
1 if (child == null) { 2 handled = super.dispatchTouchEvent(event); 3 } else { 4 handled = child.dispatchTouchEvent(event);
里面有这么一段,所以会调用MyGroupViewB.dispatchTouchEvent()方法,成功将事件传递给子View。MyGroupViewB按同样的步骤将事件传递给MyView(期间打印了ViewGroupB dispatchTouchEvent0, ViewGroupB onInterceptTouchEvent0)。MyView的dispatchTouchEvent()方法被调用(打印View dispatchTouchEvent0),然后调用super.dispatchTouchEvent()方法即View.dispatchEvent()方法,我们看下该方法片段:
code5:
1 ListenerInfo li = mListenerInfo; 2 if (li != null && li.mOnTouchListener != null 3 && (mViewFlags & ENABLED_MASK) == ENABLED 4 && li.mOnTouchListener.onTouch(this, event)) { 5 result = true; 6 } 7 8 if (!result && onTouchEvent(event)) { 9 result = true; 10 }
如果设置了OnTouchListeener则会优先调用onTouch()方法,该方法的返回这也会影响是否会调用onTouchEvent()。这里我们没有设置OnTouchListener则会调用第8行的onTouchEvent()方法(打印View onTouchEnent0),接着调用super.onTouchEvent方法,即View.onTouchEvent()方法,第8行看到该方法的返回值会影响result的值,若返回true则result为true从而导致dispatchTouchEvent()方法返回true,不改写的情况下返回false。该方法返回后回到MyGroupViewB.dispathTouchEvent()方法的65行,因为返回false,所以不进入for循环,跳出循环,进入如下代码片段:
code6:
1 if (mFirstTouchTarget == null) { 2 // No touch targets so treat this as an ordinary view. 3 handled = dispatchTransformedTouchEvent(ev, canceled, null, 4 TouchTarget.ALL_POINTER_IDS);
mFirstTouchTarget==null成立,再次进入dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS)方法,因为传入的第三个参数是null,根据code4所以这次会调用super.dispatchTouchEvent()方法,即View.dispatchTouchEvent()方法,这里会打印:ViewGroupB onTouchEvent0。然后从MyGroupViewB.dispathTouchEvent()方法返回,进入MyGroupViewA.dispathTouchEvent()方法的65行往下执行(和MyGroupViewB类似)期间打印:ViewGroupA onTouchEvent0。这样就从MyGroupViewA.dispathTouchEvent()返回了,事件分发在这三个控件的部分也就结束了。
实验二:
将MyGroupViewA的onInterceptedTouchEvent()方法直接返回true。
结果:
01-19 18:00:02.768 23804-23804/com.dhn.touchevnettest D/dhn﹕ ViewGroupA dispatchTouchEvent0
01-19 18:00:02.828 23804-23804/com.dhn.touchevnettest D/dhn﹕ ViewGroupA dispatchTouchEvent2
01-19 18:00:02.838 23804-23804/com.dhn.touchevnettest D/dhn﹕ ViewGroupA dispatchTouchEvent1
分析:
MyGroupViewA.dispatchTouchEvent()方法中code1第九行intercepted = onInterceptTouchEvent(ev)直接将intercepted置为true,向下执行code3第一行if判断失败,就不会进入事件分发的for循环code3第32行,也就意味着不进行事件在MyGroupViewA这里被截断,不继续往MyGroupViewB分发。直接进入code6。
实验三:
MyView的onTouchEvent()直接返回true。
结果:
01-19 18:12:04.688 29980-29980/com.dhn.touchevnettest D/dhn﹕ ViewGroupA dispatchTouchEvent0
01-19 18:12:04.688 29980-29980/com.dhn.touchevnettest D/dhn﹕ ViewGroupA onInterceptTouchEvent0
01-19 18:12:04.688 29980-29980/com.dhn.touchevnettest D/dhn﹕ ViewGroupB dispatchTouchEvent0
01-19 18:12:04.688 29980-29980/com.dhn.touchevnettest D/dhn﹕ ViewGroupB onInterceptTouchEvent0
01-19 18:12:04.688 29980-29980/com.dhn.touchevnettest D/dhn﹕ View dispatchTouchEvent0
01-19 18:12:04.688 29980-29980/com.dhn.touchevnettest D/dhn﹕ View onTouchEnent0
分析:
该实验事件分发同实验一,不同的是当执行到MyView.onTouchEvent()方法时,直接返回true。正如code6第8行所示,result被设置为true,导致dispatchTouchEvent()返回true,返回到MyGroupViewB.dispatchTouchEvent()方法时会进入for循环(code3第65行),设置mFirstTouchTarget != null,这样code6第一行的判断就为假,不会调用dispatchTransformedTouchEvent(ev, canceled, TouchTarget.ALL_POINTER_IDS)也就不会调用MyGroupViewB.onTouchEvent(),接着MyGroupViewB.dispatchTouchEvent()返回true,返回进入MyGroupViewA.dipatchTouchEvent()时同样不会调用onTouchEvent()方法。
参考:《Android群英传》-徐宜生
标签:
原文地址:http://www.cnblogs.com/gatsbydhn/p/5143256.html