Android View事件分发机制 二(ViewGroup)
2017-09-12 14:23
537 查看
在上篇文章中,我们讨论了
自定义LinearLayout类
主布局文件:
主
可以发现,点击button时,触发的流程是:
事件2: 点击
可以看到,点击
这时需要我们从
2.1
1. 19行-25 行,对
由于
其中
默认的
参考资料:
接着会有1个
第80行的
第85行调用
字view的先序遍历顺序,其中先序遍历顺序是按view的绘制顺序排列的(按照View的Z坐标由小到大排列)
第89行进入1个
第103行调用
第114行判断getTouchTarget返回的对象是否为null,如果不为null表示当前view已经接收了自已区域内的响应事件直接break出循环。
第122行调用
第122行到141行,如果调用
149行到第159行之间的为for循环遍历
163行到166行,如果
第174-176行代码是对ACTION_DOWN事件和
第8行到17行,对
第35行到70行,对 事件进行处理,如果第3个参数
参考资料:
http://blog.csdn.net/yanbober/article/details/45912661
View的事件分发机制。这篇文章我们讨论布局控件
ViewGroup的事件分发机制。
ViewGroup继承自
View组件。
一、小例子分析
这次由于要讨论布局控件ViewGroup,所以这次我们重写
Button、
LinearLayout两个控件。
1.项目源码
自定义Button类public class TestButton extends Button { public static final String CLICK_EXAMPLE_3 = "ClickExample3"; public TestButton(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.i(CLICK_EXAMPLE_3,"TestButton dispatchTouchEvent--action= "+event.getAction()); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(CLICK_EXAMPLE_3,"TestButton onTouchEvent--action= "+event.getAction()); return super.onTouchEvent(event); } }
自定义LinearLayout类
public class TestLinearLayout extends LinearLayout { public static final String CLICK_EXAMPLE_3 = "ClickExample3"; public TestLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i(CLICK_EXAMPLE_3,"TestLinearLayout onInterceptTouchEvent--action= "+ev.getAction()); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(CLICK_EXAMPLE_3,"TestLinearLayout onTouchEvent--action= "+event.getAction()); return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(CLICK_EXAMPLE_3,"TestLinearLayout dispatchTouchEvent--action= "+ev.getAction()); return super.dispatchTouchEvent(ev); } }
主布局文件:
<?xml version="1.0" encoding="utf-8"?> <com.lengyu.free.clickexample3.TestLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/my_linear" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" > <com.lengyu.free.clickexample3.TestButton android:id="@+id/my_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Click Me!"/> </com.lengyu.free.clickexample3.TestLinearLayout>
主
Activity文件
public class MainActivity extends Activity implements View.OnClickListener,View.OnTouchListener{ public static final String CLICK_EXAMPLE_3 = "ClickExample3"; private TestLinearLayout mLayout; private TestButton mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton=(TestButton)this.findViewById(R.id.my_btn); mLayout=(TestLinearLayout)this.findViewById(R.id.my_linear); mButton.setOnClickListener(this); mLayout.setOnClickListener(this); mButton.setOnTouchListener(this); mLayout.setOnTouchListener(this); } @Override public void onClick(View v) { Log.i(CLICK_EXAMPLE_3,"OnClickListener--onClick--"+v); } @Override public boolean onTouch(View v, MotionEvent event) { Log.i(CLICK_EXAMPLE_3,"OnTouchListener--onTouch--action="+event.getAction()+"--"+v); return false; } }
2.事件分析
事件1: 点击Button区域:
09-12 02:21:11.127 2292-2292/? I/ClickExample3: TestLinearLayout dispatchTouchEvent--action= 0 09-12 02:21:11.127 2292-2292/? I/ClickExample3: TestLinearLayout onInterceptTouchEvent--action= 0 09-12 02:21:11.127 2292-2292/? I/ClickExample3: TestButton dispatchTouchEvent--action= 0 09-12 02:21:11.127 2292-2292/? I/ClickExample3: OnTouchListener--onTouch--action=0--com.lengyu.free.clickexample3.TestButton{b2f60670 VFED..C. ......I. 166,345-313,417 #7f0c0051 app:id/my_btn} 09-12 02:21:11.127 2292-2292/? I/ClickExample3: TestButton onTouchEvent--action= 0 09-12 02:21:11.177 2292-2292/? I/ClickExample3: TestLinearLayout dispatchTouchEvent--action= 1 09-12 02:21:11.177 2292-2292/? I/ClickExample3: TestLinearLayout onInterceptTouchEvent--action= 1 09-12 02:21:11.177 2292-2292/? I/ClickExample3: TestButton dispatchTouchEvent--action= 1 09-12 02:21:11.177 2292-2292/? I/ClickExample3: OnTouchListener--onTouch--action=1--com.lengyu.free.clickexample3.TestButton{b2f60670 VFED..C. ...P..I. 166,345-313,417 #7f0c0051 app:id/my_btn} 09-12 02:21:11.177 2292-2292/? I/ClickExample3: TestButton onTouchEvent--action= 1 09-12 02:21:11.177 2292-2292/? I/ClickExample3: OnClickListener--onClick--com.lengyu.free.clickexample3.TestButton{b2f60670 VFED..C. ...P..I. 166,345-313,417 #7f0c0051 app:id/my_btn}
可以发现,点击button时,触发的流程是:
TestLinearLayout.dispatchTouchEvent->
TestLinearLayout.onInterceptTouchEvent->
TestButton.dispatchTouchEvent,也就是说点击
TestButton时,分发的每一个事件都是由
TestButton的父控件
TestLinearLayout分发给子控件。
事件2: 点击
Button区域之外的区域:
09-12 02:54:56.138 2292-2292/? I/ClickExample3: TestLinearLayout dispatchTouchEvent--action= 0 09-12 02:54:56.138 2292-2292/? I/ClickExample3: TestLinearLayout onInterceptTouchEvent--action= 0 09-12 02:54:56.138 2292-2292/? I/ClickExample3: OnTouchListener--onTouch--action=0--com.lengyu.free.clickexample3.TestLinearLayout{b2f5fee8 V.E...C. ......I. 0,0-480,762 #7f0c0050 app:id/my_linear} 09-12 02:54:56.138 2292-2292/? I/ClickExample3: TestLinearLayout onTouchEvent--action= 0 09-12 02:54:56.218 2292-2292/? I/ClickExample3: TestLinearLayout dispatchTouchEvent--action= 1 09-12 02:54:56.218 2292-2292/? I/ClickExample3: OnTouchListener--onTouch--action=1--com.lengyu.free.clickexample3.TestLinearLayout{b2f5fee8 V.E...C. ...P..I. 0,0-480,762 #7f0c0050 app:id/my_linear} 09-12 02:54:56.218 2292-2292/? I/ClickExample3: TestLinearLayout onTouchEvent--action= 1 09-12 02:54:56.218 2292-2292/? I/ClickExample3: OnClickListener--onClick--com.lengyu.free.clickexample3.TestLinearLayout{b2f5fee8 V.E...C. ...P..I. 0,0-480,762 #7f0c0050 app:id/my_linear}
可以看到,点击
button之外的区域时,
ACTION_DOWN事件的分发顺序是
dispatchTouchEvent->
onInterceptTouchEvent->
onTouch->
onTouchEvent。
ACTION_UP事件的分发顺序是
dispatchTouchEvent->
onTouch->
onTouchEvent-
onClick,在执行
ACTION_UP事件时,
onInterceptTouchEvent并没有执行。
这时需要我们从
ViewGroup中寻找答案。
二、ViewGroup源码分析
ViewGroup继承自
View,对其中的部分方法进行了复写。下面我们先来分析一下
dispatchTouchEvent方法
2.1 dispatchTouchEvent
方法
ViewGroup中
dispatchTouchEvent方法如下:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } // If the event targets the accessibility focused view and this is it, start // normal event dispatch. Maybe a descendant is what will handle the click. if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) { ev.setTargetAccessibilityFocus(false); } 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. 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) { 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; } // 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; if (!canceled && !intercepted) { // If the event is targeting accessiiblity focus we give it to the // view that has accessibility focus and if it does not handle it // we clear the flag and dispatch the event to all children as usual. // We are looking up the accessibility focused host to avoid keeping // state since these events are very rare. 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; 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 there is a view that has accessibility focus we want it // to get the event first and if not handled we will perform a // normal dispatch. We may do a double iteration but this is // safer given the timeframe. 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); 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(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } // The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. 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; } } } // 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; }
1. 19行-25 行,对ACTION_DOWN
事件进行处理。
由于ACTION_DOWN事件是一系类事件的开端,所以在首次分发
ACTION_DOWN事件时,调用
cancelAndClearTouchTargets(ev);方法,对
ACTION_DOWN事件之前的手势进行清理取消,并将
mFirstTouchTarget的值设为
NULL。然后调用
resetTouchState方法,将触摸状态重置。
2. 28 行-42 行,检测是否需要拦截
在该处代码中,先声明了一个intercepted变量,用来标记是否需要拦截。然后在if判断中,判断
actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null, 如果是
ACTION_DOWN或者
mFirstTouchTarget != null(即已经找到能够接收touch事件的目标组件)时,条件成立,成立后接下来获取
mGroupFlags中 的
disallowIntercept(禁止拦截)状态,如果
disallowIntercept(禁止拦截)的值的状态为
false的话,即允许拦截,则调用
onInterceptTouchEvent方法获取用户自定义的拦截状态设置给
intercepted变量,并将action的行为进行存储。如果
disallowIntercept(禁止拦截)的值的状态为
true的话,即不允许拦截的话,则直接设置
intercepted变量的值为
false。如果
actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null不成立,即尚未找到触摸对象且不是初始化的
ACTION_DOWN事件,则直接进行拦截。
其中
disallowIntercept(禁止拦截) 状态的设置可用
requestDisallowInterceptTouchEvent方法
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { // We're already in this state, assume our ancestors are too return; } if (disallowIntercept) { mGroupFlags |= FLAG_DISALLOW_INTERCEPT; } else { mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } // Pass it up to our parent if (mParent != null) { mParent.requestDisallowInterceptTouchEvent(disallowIntercept); } }
requestDisallowInterceptTouchEvent方法设置我
true时,
intercepted的值为
false。ViewGroup不同于View特有的
onInterceptTouchEvent方法
public boolean onInterceptTouchEvent(MotionEvent ev) { return false; }
默认的
onInterceptTouchEvent方法只是返回了一个
false,也即
intercepted=false. 上面例子的调用流程为
dispatchTouchEvent->
onInterceptTouchEvent->
onTouchEvent,也就是说
disallowIntercept的默认值为
false.
参考资料:
3. 51 行 到 52行
通过标记和action检查cancel,然后将结果赋值给局部boolean变量canceled。4. 53-函数结束,事件分发。
54行首先可以看见获取一个boolean变量标记split来标记,默认是true,作用是是否把事件分发给多个子View,这个同样在ViewGroup中提供了public的方法设置,如下:public void setMotionEventSplittingEnabled(boolean split) { // TODO Applications really shouldn't change this setting mid-touch event, // but perhaps this should handle that case and send ACTION_CANCELs to any child views // with gestures in progress when this is changed. if (split) { mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; } else { mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS; } }
接着会有1个
if(!canceled && !intercepted)判断语句,当
canceled和
intercepted的值都为
false时,会进入
if其中。
第80行的
if判断中对
newTouchTarget和
childrenCount进行判断,如果
newTouchTarget为空和
childrenCount个数不为空的话,则进一步处理。
第85行调用
buildOrderedChildList方法返回
ViewGroup中
字view的先序遍历顺序,其中先序遍历顺序是按view的绘制顺序排列的(按照View的Z坐标由小到大排列)
第89行进入1个
for循环,从
buildOrderedChildList序列的最后一个
view向前遍历,视觉上是从前往后进行扫描,即对于多个包含同一点的
view, 浮在最上面的view最先进行相应
第103行调用
getTouchTarget(child)方法来判断当前view是否已经在
mFirstTouchTarget变量所指向的
TouchTarget链表中,如果在的还返回该对象,如果不在的返回null.
第114行判断getTouchTarget返回的对象是否为null,如果不为null表示当前view已经接收了自已区域内的响应事件直接break出循环。
第122行调用
dispatchTransformedTouchEvent方法对传递的事件进行分发,该方法如果第3个参数
child控件的值为null的话,即
ViewGroup的子
view为空,调用父控件(View)的
dispatchTouchEvent方法。子
view为不为空,调用子控件的
dispatchTouchEvent方法。该方法返回调用
dispatchTouchEvent方法的布尔值。
第122行到141行,如果调用
dispatchTransformedTouchEvent方法时子
view或父
view中
dispatchTouchEvent返回的值为
true话,(这时子View中的
onTouchEvent方法已经消费掉该事件返回true)则进入if条件语句中,在该if条件从句中
mLastTouchDownIndex记录最后
child控件的坐标, 调用
addTouchTarget方法将当前子View加入到
mFirstTouchTarget变量指向的链表中,
newTouchTarget记录当前
TouchTarget, 将
alreadyDispatchedToNewTouchTarget方法设置为
true;跳出当前遍历子
view循环。如果调用
dispatchTransformedTouchEvent方法时子
view或父
view中
dispatchTouchEvent返回的值为
false,这时
newTouchTarget为null,
alreadyDispatchedToNewTouchTarget变量的值为
false,无法调用addTouchTarget(),从而导致
mFirstTouchTarget为null(没法对mFirstTouchTarget赋值,因为上面分析了
mFirstTouchTarget一进来是
ACTION_DOWN就置位为
null了),这时再执行下面的
ACTION_MOVE事件和
ACTION_UP事件时,执行到28行到42行时,
mFirstTouchTarget为null导致
onInterceptTouchEvent方法不会执行,
intercepted的值
true,再执行到58行时,直接跳到163行执行,把ViewGroup当作普通view调用
dispatchTransformedTouchEvent方法处理事件。
149行到第159行之间的为for循环遍历
View之外以及条件
if (newTouchTarget == null && childrenCount != 0)之后的代码,该处代码判断如果newTouchTarget 为null,但mFirstTouchTarget不为空,即没有找到子view接收控件,但
mFirstTouchTarget不为空,就将
newTouchTarget指向
mFirstTouchTarget链表中最后一个添加的对象。
163行到166行,如果
mFirstTouchTarget=null,即ViewGroup没有找到能够消费子控件的view对象,则把当前ViewGroup当作普通控件调用父
view的
dispatchTouchEvent方法进行处理。该种情况与上面事件2(点击 Button区域外的区域)情况相同,结合29行到42行代码解释了为什么
ACTIONDOWN事件调用了
onInterceptTouchEvent方法,
ACTIONDOWNUP没有调用。同时也解释了上文章中的事件9,由于子view中处理
ACTION_DOWN事件时,
dispatchTouchEvent方法返回为
false, 导致
dispatchTransformedTouchEvent返回为
false,没有对
mFirstTouchTarget进行赋值为null,在该处代码中将
ACTION_DOWN事件分发到父
View中的
dispatchTouchEvent方法中,多打出父
View中
ACTION_DOWN事件日志。
第174-176行代码是对ACTION_DOWN事件和
dispatchTransformedTouchEvent返回为
true的处理,这时
mFirstTouchTarget!=null已经找到消费该事件的子
View,故直接返回
true,不再调用父VIew中的
dispatchTouchEvent方法。第176-195行代码,则是对已经找到消费该事件子view,除
ACTIONDOWN事件之外的其它事件进行处理,接着递归调用子view的
dispatchTouchEvent方法
2.1 dispatchTransformedTouchEvent
dispatchTransformedTouchEvent方法源码如下private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; // Canceling motions is a special case. We don't need to perform any transformations // or filtering. The important part is the action, not the contents. 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()); } handled = child.dispatchTouchEvent(transformedEvent); } // Done. transformedEvent.recycle(); return handled; }
第8行到17行,对
ACTION_CANCEL事件进行处理,如果第3个参数
child控件的值为null的话,即
ViewGroup的子
view为空,调用父控件(View)的
dispatchTouchEvent方法。子
view为不为空,调用子控件的
dispatchTouchEvent方法
第35行到70行,对 事件进行处理,如果第3个参数
child控件的值为null的话,即
ViewGroup的子
view为空,调用父控件(View)的
dispatchTouchEvent方法。子
view为不为空,调用子控件的
dispatchTouchEvent方法。该方 法返回调用
dispatchTouchEvent方法的布尔值。
参考资料:
http://blog.csdn.net/yanbober/article/details/45912661
相关文章推荐
- 转:Android ViewGroup事件分发机制
- Android事件分发机制--ViewGroup(二)
- Android ViewGroup事件分发机制
- Android ViewGroup事件分发机制
- Android 的事件分发机制(二)---ViewGroup的事件传递
- 跟着郭神写博客——Android事件分发机制ViewGroup完全解析
- [Android] View和ViewGroup事件分发机制
- Android ViewGroup事件分发机制
- Android开发总结笔记 ViewGroup的事件分发机制 3-10
- Android View、ViewGroup 事件分发机制(一)
- Android View和ViewGroup事件分发机制源码分析
- Android View 事件分发机制 && Android ViewGroup 事件分发机制 源码解析 --总结
- Android ViewGroup事件分发机制(requestDisallowInterceptTouchEvent方法)
- Android ViewGroup事件分发机制
- Android ViewGroup事件分发机制
- Android之ViewGroup事件分发机制
- Android ViewGroup事件分发机制总结
- Android ViewGroup事件分发机制
- Android ViewGroup事件分发机制
- Android ViewGroup 事件分发机制详解