ViewGroup事件分发流程图:

ViewGroup事件分发源码解析:
ViewGroup.java
// 触摸目标链表中的第一个目标
// 什么情况下,才会有多个触摸目标那? 多点触控时,就会形成链.
private TouchTarget mFirstTouchTarget
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction()
// 0001 & 1111 = 0001
final int actionMasked = action & MotionEvent.ACTION_MASK
// down事件是整个事件流程(DOWN -> MOVE -> UP)的第一步
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 重置与清除
cancelAndClearTouchTargets(ev)
resetTouchState()
}
// 检测是否拦截
final boolean intercepted
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 当child调用 getParent().requestDisallow(),
// 是否希望过滤父级 viewGroup 的 onInterceptTouchEvent
// 默认执行 viewGroup.onInterceptTouchEvent()
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0
if (!disallowIntercept) { // 如果允许拦截,则执行拦截方法
intercepted = onInterceptTouchEvent(ev)
ev.setAction(action)
} else { // child 不希望父view 拦截
intercepted = false
}
} else {
// 假如分发的down事件,child没有处理, 则后续的事件(move->up),child 是没有机会处理的(给你机会,你不珍惜啊~)
intercepted = true
}
// 是否取消
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
// 没有cancel且不拦截,则向child分发事件
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// 多点触摸: 一根手指具有唯一的 pointerId,
// actionIndex(可以理解为手指的数量) 却是动态变化的
final int actionIndex = ev.getActionIndex()
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS
// 过滤上一个手指的影响
removePointersFromTouchTargets(idBitsToAssign)
// 查找可处理该事件的child
final int childrenCount = mChildrenCount
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex)
final float y = ev.getY(actionIndex)
// 根据Zorder值 创建 child集合
final ArrayList<View> preorderedList = buildTouchDispatchChildList()
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled()
final View[] children = mChildren
// 从后往前遍历childrens. 因为 Zorder 越大,越靠近用户
for (int i = childrenCount - 1
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder)
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex)
// child 不可见且不在手指点击的范围内,则向下查找
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false)
continue
}
// 如果已有的触摸目标的 child与之相等,则说明找到了
newTouchTarget = getTouchTarget(child)
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign
break
}
resetCancelNextUpFlag(child)
// child得到这次事件的处理权, 调用 view.dispatchTouchEvent
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// child消费了这个事件
mLastTouchDownTime = ev.getDownTime()
// 查找 child 在 childrens 中的位置
if (preorderedList != null) {
for (int j = 0
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j
break
}
}
} else {
mLastTouchDownIndex = childIndex
}
mLastTouchDownX = ev.getX()
mLastTouchDownY = ev.getY()
// 插入触摸链表的头部
newTouchTarget = addTouchTarget(child, idBitsToAssign)
alreadyDispatchedToNewTouchTarget = true
break
}
ev.setTargetAccessibilityFocus(false)
}
if (preorderedList != null) preorderedList.clear()
}
// 如果除第一次触摸到的 child 外,其他的child不消费down事件, 则使用第一个 child
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next
}
newTouchTarget.pointerIdBits |= idBitsToAssign
}
}
}
if (mFirstTouchTarget == null) {
// 没有找到消费该事件的 child, 则交给自己处理
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS)
} else {
// 分发事件到触摸目标 child
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
// 事件经过转换后分发给 child处理
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
}
}
// 如果viewGroup收到cancel或者up事件, 则重置触摸链
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
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled
// 处理cancel事件
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
}
// 计算 pointerId
final int oldPointerIdBits = event.getPointerIdBits()
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits
// 我们想要的指针与事件的指针不一样,则放弃
if (newPointerIdBits == 0) {
return false
}
final MotionEvent transformedEvent
if (newPointerIdBits == oldPointerIdBits) { // 单点触控下,事件的id与想要的id一致
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)
// 事件原样分发给child
handled = child.dispatchTouchEvent(event)
event.offsetLocation(-offsetX, -offsetY)
}
return handled
}
transformedEvent = MotionEvent.obtain(event)
} else { // 多点触控下, 事件需要经过分割,只包含对应 pointer 的事件
// 例如
// ACTION_UP 和 ACTION_MOVE
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())
}
// 分割转换后的事件分发给 child
handled = child.dispatchTouchEvent(transformedEvent)
}
// Done.完成,回收事件
transformedEvent.recycle()
return handled
}
如何验证触摸目标的数量,就是屏幕上手指的数量
XXActivity.kt
private fun printTouchTarget(view: ViewGroup = findViewById<ParentGroup>(R.id.event_container)) {
val mFirstTouchTarget = ViewGroup::class.java.getDeclaredField("mFirstTouchTarget")
mFirstTouchTarget.isAccessible = true
val firstTouchTargetRef = mFirstTouchTarget.get(view)
Log.e("XXX", "firstTouchTargetRef :------${firstTouchTargetRef}")
if (firstTouchTargetRef != null) {
val innerClass = ViewGroup::class.java.declaredClasses as Array<Class<Any>>
innerClass.forEach {
if (it.name == "android.view.ViewGroup$TouchTarget") {
try {
val nextField = it.getDeclaredField("next")
var next = nextField.get(firstTouchTargetRef)
while (next != null) {
Log.e("XXX", "next :------${next}")
next = nextField.get(next)
}
}catch(exp: Exception){}
}
}
}