本文是学习Android事件分发机制的学习笔记,一是为了巩固学习成果,加深印象;二是为了方便以后查阅。
Activity对事件的分发过程
从Activity#dispatchTouchEvent()
开始看起:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
首先对ACTION_DOWN
事件进行了特殊判断,调用onUserInteraction()
,跟进这个方法,会发现是一个空方法:
public void onUserInteraction() {
}
不去管它,接下来Activity
会通过getWindow()
获得自己所属的Window
进行分发,Window
是个抽象类,用来控制顶级View的外观和行为策略,它的唯一实现类是PhoneWindow
。那么PhoneWindow
是如何处理点击事件的,PhoneWindow#superDispatchTouchEvent()
如下所示:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
很简单,直接传递给了mDecor
,这个mDecor
就是当前窗口最顶层的DecorView
。
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
跟进DecorView#superDispatchTouchEvent()
:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
居然是调用父类的dispatchTouchEvent()
方法,DecorView
的父类是FrameLayout
,继续跟进查看,发现FrameLayout
并没有这个方法,那就继续向上追,FrameLayout
的父类是ViewGroup
,也就是说,触摸事件经过层层传递,最终传递到ViewGroup#dispatchTouchEvent()
方法,至此,事件已经传递到视图的顶级View了。
ViewGroup对事件的分发过程
接下来是重头戏了...上代码ViewGroup#dispatchTouchEvent()
...
public boolean dispatchTouchEvent(MotionEvent ev) {
// 调试用,不去管它
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// 辅助功能,有些用户由于视力上、身体上、年龄上使他们不能接受语音或者视觉信息
// 不是重点,也不去管它
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
// onFilterTouchEventForSecurity(ev),触摸事件安全过滤
// 具体实现:当窗口被遮挡,返回false,丢弃触摸事件;未被遮挡,返回true
if (onFilterTouchEventForSecurity(ev)) {
// 没有被遮挡
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 如果是Down事件,则重置所有之前保存的状态,因为这是事件序列的开始
// mFirstTouchTarget会被设为Null
cancelAndClearTouchTargets(ev);
// 重置FLAG_DISALLOW_INTERCEPT
resetTouchState();
}
// 检测是否拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 标记事件不允许被拦截,默认为false
// 可以由requestDisallowInterceptTouchEvent方法来设置
// 设置为true,ViewGroup将无法拦截Down以外的点击事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 调用onInterceptTouchEvent(ev)方法,询问自己是否要拦截事件
// ViewGroup的onInterceptTouchEvent(ev)方法默认返回false
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);
}
// 通过标记和Action检查Cancel,将结果赋值给局部变量canceled
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// split标记是否需要将事件分发给多个子View,默认为true
// 可通过setMotionEventSplittingEnabled()方法设置
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
// 如果没取消也没拦截,进入执行语句中
if (!canceled && !intercepted) {
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;
// 判断newTouchTarget为Null,且ChildrenCount不为0
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// 寻找可以处理触摸事件的子View
// 通过buildTouchDispatchChildList()方法构建子View的List集合preorderedList
final ArrayList preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// 倒序遍历所有的子View
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, 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;
}
// 同时满足两种情况下子View可以接收事件的分发
// canViewReceivePointerEvents()方法会判断子View是否可见和是否在播放动画
// isTransformedTouchPointInView()方法会判断触摸事件坐标是否在子View内
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// 查找当前子View是否在mFirstTouchTarget中存储
// mFirstTouchTarget是一种单链表结构
// 找不到则返回Null
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// newTouchTarget不为Nul,说明已经找到接收的View了,break跳出for循环
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
// 没有跳出循环,说明我们找到的Child并没有在mFirstTouchTarget中
// 调用dispatchTransformedTouchEvent()方法
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();
// 将child赋值给mFirstTouchTarget
newTouchTarget = addTouchTarget(child, idBitsToAssign);
// alreadyDispatchedToNewTouchTarget赋值为true,跳出循环
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();
}
// 没有找到可以接收事件的子View,并且之前的mFirstTouchTarget不为空
// newTouchTarget指向了最初的mFirstTouchTarget
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;
}
}
}
if (mFirstTouchTarget == null) {
// 如果mFirstTouchTarget为null
// 调用dispatchTransformedTouchEvent()方法
// 第三个参数为null,会调用super.dispatchTouchEvent()方法
// 将当前ViewGroup当做普通的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;
}
事件拦截
intercepted
用来标记ViewGroup
是否拦截事件,当事件为MotionEvent.ACTION_DOWN
或者mFirstTouchTarget!=null
时,if
判断成立,然后判断disallowIntercept
标志位,当disallowIntercept
为false
时,调用onInterceptTouchEvent()
方法,并将返回值赋值给intercepted
,否则当disallowIntercept
为true
时,则直接将intercepted
赋值为false
。
disallowIntercept
标记位可以通过公共方法 requestDisallowInterceptTouchEvent()
设置,通常由子View
调用,当设置为true
后,ViewGroup
将无法拦截除ACTION_DOWN
之外的点击事件,原因是当事件为ACTION_DOWN
时,ViewGroup
会重置disallowIntercept
标记位,并且将mFirstTouchTarget
设置为null
,因此,当事件为ACTION_DOWN
时,ViewGroup
总是会调用自己的onInterceptTouchEvent()
方法。
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 如果是Down事件,则重置所有之前保存的状态,因为这是事件序列的开始
// mFirstTouchTarget会被设为Null
cancelAndClearTouchTargets(ev);
// 重置FLAG_DISALLOW_INTERCEPT
resetTouchState();
}
// 检测是否拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 标记事件不允许被拦截,默认为false
// 可以由requestDisallowInterceptTouchEvent方法来设置
// 设置为true,ViewGroup将无法拦截Down以外的点击事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 调用onInterceptTouchEvent(ev)方法,询问自己是否要拦截事件
// ViewGroup的onInterceptTouchEvent(ev)方法默认返回false
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;
}
事件分发
中间经过标记和action
检查cancel
,将结果赋值给变量canceled
。if (!canceled && !intercepted)
语句表明,事件未被取消且intercepted
为false
(未拦截),则会进入执行语句中。
首先判断childrenCount
不为0
,然后通过buildTouchDispatchChildList()
方法拿到子元素的List
集合,接着倒序遍历所有子元素,寻找可以接收点击事件的子元素,为什么要倒序遍历,是因为buildTouchDispatchChildList()
内部会调用buildOrderedChildList()
方法,该方法会将子元素根据Z
轴排序,在同一Z
平面上的子元素则会根据绘制的先后顺序排序,触摸的时候我们当然会希望浮在最上层的元素最先响应事件。
对于每一个子元素来说,需要canViewReceivePointerEvents()
和isTransformedTouchPointInView()
均返回true
,才说明该子元素可以处理触摸事件,否则直接continue
进行下一次循环。
canViewReceivePointerEvents()
通过是否可见及是否在播放动画来判断子元素是否可以接收事件,isTransformedTouchPointInView()
判断触摸事件的坐标点是否在子元素内,这样我们就获得了可以处理触摸事件的子元素。
接下来通过getTouchTarget()
方法判断当前子元素是否已经赋值给mFirstTouchTarget
,如果newTouchTarget
不为null
,说明子元素已经在mFirstTouchTarget
中,执行break
跳出循环。
如果newTouchTarget
为null
,说明子元素并没有在mFirstTouchTarget
中保存,此时调用dispatchTransformedTouchEvent()
方法,该方法十分重要,在该方法内部:如果子元素是ViewGroup
并且事件没有被拦截,那么递归调用ViewGroup
的dispatchTouchEvent()
;如果子元素是View
,那么调用View
的dispatchTouchEvent()
,最终会调用View
的onTouchEvent()
。
dispatchTransformedTouchEvent()
方法是有返回值的,如果返回true
,说明子元素消耗了触摸事件,则在下面的代码中将子元素赋值给mFirstTouchEvent
,并跳出循环,mFirstTouchTarget
是否被赋值,将直接影响到ViewGroup
对事件的拦截策略,如果mFirstTouchTarget
为null
,那么ViewGroup
就会默认拦截接下来同一序列中的所有触摸事件,这一点在后面分析;如果返回false
,ViewGroup
就会把事件分发给下一个子元素(如果还有下一个子元素的话)。
// 如果没取消也没拦截,进入方法体中
if (!canceled && !intercepted) {
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;
// 判断newTouchTarget为Null,且ChildrenCount不为0
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// 寻找可以接受触摸事件的子View
// 通过buildTouchDispatchChildList()方法构建子View的List集合preorderedList
final ArrayList preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// 倒序遍历所有的子View
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, 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;
}
// 同时满足两种情况下子View可以接收事件的分发
// canViewReceivePointerEvents()方法会判断子View是否可见和是否在播放动画
// isTransformedTouchPointInView()方法会判断触摸事件坐标是否在子View内
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// 查找当前子View是否在mFirstTouchTarget中存储
// 找不到则返回Null
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// newTouchTarget不为Nul,说明已经找到接收的View了,break跳出for循环
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
// 没有跳出循环,说明我们找到的Child并没有在mFirstTouchTarget中
// 调用dispatchTransformedTouchEvent()方法
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();
// 将child赋值给mFirstTouchTarget
newTouchTarget = addTouchTarget(child, idBitsToAssign);
// alreadyDispatchedToNewTouchTarget赋值为true,跳出循环
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();
}
当没有任何子元素处理触摸事件时,调用dispatchTransformedTouchEvent()
方法,注意此时第三个参数传入null
,在方法内部就会调用super.dispatchTouchEvent()
,也就是View
类的dispatchTouchEvent()
。
if (mFirstTouchTarget == null) {
// 如果mFirstTouchTarget为null
// 调用dispatchTransformedTouchEvent()方法
// 第三个参数为null,会调用super.dispatchTouchEvent()方法
// 将当前ViewGroup当做普通的View处理
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
onInterceptTouchEvent()
方法
if
语句判断触摸事件来源是否为鼠标或其他指针式设备,其他情况下默认返回false
。
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
dispatchTransformedTouchEvent()
方法
dispatchTransformedTouchEvent()
源码中发现多次对于传入的child
是否为null
做判断,并且都做类似的操作:
当child==null
时,调用super.dispatchTouchEvent()
,也就是View
类的dispatchTouchEvent()
方法,因为ViewGroup
的父类是View
,最终会调用View
的onTouchEvent()
方法。
当child!=null
时,递归调用child.dispatchTouchEvent()
,此时child
可能是View
,也可能是ViewGroup
。
从源码中可以看出dispatchTransformedTouchEvent()
方法的返回值,最终取决于onTouchEvent()
方法,也就是说,onTouchEvent()
是否消费了事件,决定了dispatchTransformedTouchEvent()
方法的返回值,从而决定mFirstTouchTarget
是否为null
。因为如果dispatchTransformedTouchEvent()
方法的返回值为false
,就无法执行addTouchTarget()
方法,而mFirstTouchTarget
就是在该方法中被赋值的。
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;
}
重要结论
-
ViewGroup
一旦拦截ACTION_DOWN
事件,那么当ACTION_MOVE
、ACTION_UP
事件到来时,将不再调用ViewGroup
的onInterceptTouchEvent()
方法,并且同一序列中的其他事件都会默认交给它处理。分析:
ViewGroup
拦截ACTION_DOWN
事件,会导致intercepted
为true
,从而导致if (!canceled && !intercepted)
判断不成立,跳过执行语句,mFirstTouchTarget
也为null
。那么当ACTION_MOVE
、ACTION_UP
事件到来时,if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null)
判断语句不成立,会直接将intercepted
赋值为true
,即默认拦截后续的所有事件。 -
某个
View
一旦开始处理事件,如果它不消费ACTION_DOWN
事件,那么同一事件序列中的其他事件也不会再交给它来处理,并且事件将重新交给它的父容器去处理。分析:某个
View
不消费ACTION_DOWN
事件,即dispatchTransformedTouchEvent()
方法返回了false
,导致if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign))
判断语句不成立,跳过执行语句,同样导致mFirstTouchTarget
为null
,那么和第一条结论的分析一样,当ACTION_MOVE
、ACTION_UP
事件到来时,if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null)
判断语句不成立,同样会直接将intercepted
赋值为true
,所以后续的事件都无法传递到这个View
,而是交给ViewGroup
处理。 ViewGroup
(绝大多数情况下)默认不拦截任何事件。Android
源码中ViewGroup
的onInterceptTouchEvent()
方法默认返回false
。ViewGroup
没有重写父类View
的onTouchEvent()
方法。
- 到这里就分析完了,查了很多资料,中间也有可能有理解错误的地方,如果哪里错了,还请大家指正,谢谢。
- Github