@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.
// 辅助功能,用于辅助有障碍人群使用;
// 如果这个事件是辅助功能事件,那么他会带有一个target view,要求事件必须分发给该view
// 如果setTargetAccessibilityFocus(false),表示取消辅助功能事件,按照常规的事件分发进行
// 这里表示如果当前是目标target view,则取消标志,直接按照普通分发即可
// 后面还有很多类似的代码,都是同样的道理
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
// 对遮盖状态进行过滤
if (onFilterTouchEventForSecurity(ev)) {
// action的高9=16位表示索引值
// 低1-8为表示事件类型
// 只有down或者up事件才有索引值
final int action = ev.getAction();
// 获取真正的事件类型
final int actionMasked = action & MotionEvent.ACTION_MASK;
// 判断事件是否为ACTION_DOWN 事件
// 如果是,则初始化,把mFirstTouchTarget置为null。由于一个完整的事件序列是以DOWN开始,以UP结束,所以如果是DOWN事件,
// 那么说明是一个新的事件序列,所以需要初始化之前的状态。这里的mFirstTouchTarget非常重要,
// 后面会说到当ViewGroup的子元素成功处理事件的时候,mFirstTouchTarget会指向子元素,
// 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.
// 这里吧mFirstTouchTarget 设置为 null
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// 定义一个布尔值 判断是否拦截
// Check for interception.
final boolean intercepted;
// 如果是ACTION——DOWN 或者 mFirstTouchTarget 不为空
// 为什么要判断 比如说,如果子view消耗了ACTION_DOWN 事件,然后这里可以由View Group继续判断是否要拦截接下来的ACTION_MOVE事件之类
// 又比如 ,如果第一次DOWN 事件最终不是由子view消耗掉的,那么显然mFirstTouch 将为null,所以也就不用判断了,直接把intercepted设置为true,此后的事件都是由这个ViewGroup处理
// down事件 或者 有target的非down事件 则需要判断是否需要拦截
// 否则不需要进行拦截,因为一定是交给自己处理
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
// 此标志为子view通过request DisallowInterupr方法设置
// 禁止viewgroup拦截事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
// disallowIntercept = 是否禁用事件拦截的功能(默认为false),可通过调用requestDisallowInterceptTouchEvent() 修改
if (!disallowIntercept) {
// ViewGroup # onInterceptTouchEvent();
// 默认返回 false ,即View Group默认不拦截任何事件,如果想要让ViewGroup拦截事件,那么应该在自定义View Group中重写这个方法
//
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.
// 自己消费了down事件,那么后续的事件非down事件搜是自己处理
intercepted = true;
}
// If intercepted, start normal event dispatch. Also if there is already
// a view that is handling the gesture, do normal event dispatch.
// 如果已经被拦截,或者已经有了目标的view,取消辅助功能的target标志
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
// 判断是否需要取消
// 这里有很多种情况需要发送取消事件
// 最常见的是viewGroup拦截了子view的ACTION_MOVE事件,导致事件序列中断
// 那么需要发送cancel事件告知该view,让该view做一些状态恢复工作
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// split 表示是否需要对事件进行分裂,对应多点触摸事件
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
// newTouchTarget 如果是down 或者 pointer——down事件的 新的绑定target
// 进行判断之前,会将newTouchTarget 和 alreadyDispatchedToNewTouchTarget 置为空
TouchTarget newTouchTarget = null;
// alreadyDispatchedToNewTouchTarget 表示事件是否已经分发给targetView了
boolean alreadyDispatchedToNewTouchTarget = false;
// 判断条件: 如果不是取消事件 以及 ViewGroup不进行拦截
if (!canceled && !intercepted) {
// If the event is targeting accessibility 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.
// 如果是辅助功能事件,我们会寻找他的targetview来接收这个事件
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
// 判断条件: 事件是否是DOWN 或 POINTER_DOWN事件 表示新的手指按下了,需要寻找接收事件的view
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// 多点触控会有不同的索引,获取索引号
// 该索引位于MotionEvent中的一个数组没索引值就是数组的下标值
// 只有up或者down事件才会携带索引值
final int actionIndex = ev.getActionIndex(); // always 0 for down
// 这个整型变量记录了TouchTarget中view所对应的触控点id
// 触控点id的范围是0-31,整型变量中一个二进制为1,则对应绑定该id的触控点
// 这里根据是否需要分离,对触控点id进行记录
// 而如果不需要分离,则默认接受所有触控点的事件
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.
// down事件表示该触控点事件序列是一个新的序列
// 清除之前绑定到该触控点id的TouchTarget
removePointersFromTouchTargets(idBitsToAssign);
// 获取childrenCount 的值,表示该viewgroup 内部有多个子view
final int childrenCount = mChildrenCount;
// 如果子空间数目不为0,而且还没绑定到新的id
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.
// 从前到后创建view列表
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
// 判断是否是自定义vie顺序
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// for循环 对所有子view 进行循环遍历
// 由于 以上判断了 view group 不对事件进行拦截,那么在这里就要对viewgroup内部的子view进行遍历,一个个的找能够接受事件的子view
// 这里注意 ,它是倒序遍历的,即从最上层的子view 开始往内层遍历,这也符合我们的习惯,因为一般来说我们对屏幕触摸,肯定是希望最上层的view来形影,而不是被覆盖在底层的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.
// 如果是辅助功能事件则优先给对应的target先处理
// 如果该view不处理,再交给其他view处理
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
// 判断 触摸点的位置 是否在子view的范围内或者子view是否在在播放动画,如果不符合 则continue,表示这个view不符合条件,开始遍历下一个子view
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// 检查该子view是否在touchTarget链表中
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.
// 链表中已经存在该子view,说明只认识一个多点触摸事件
// 即两次都触摸到同一个view上
// 将新的触控点id绑定到给TouchTarget上
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
//设置取消标志
// 下次再次调用这个方法就会返回true
resetCancelNextUpFlag(child);
// dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
// 当传递进来的child 不为null时,就会调用子view 的dispatchTouchEvent(event)方法
// 子view符合所有条件的时候,事件就会在这里传递给了子view来处理,完成了 viewgroup 到 子view的事件传递,
// 当事件处理完毕,就会返回一个布尔值handled,该值表示子view死否消耗了事件
// 如果说子view的onTouchEvent()返回true,那就是消耗了事件。需要生成新的TouchTarget
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();
// addTouchTarget(child, idBitsToAssign)
// 把 mFirstTouchTarget 指向了child,同时把newTouchTarget也指向了child
// 也就是说,如果子view消耗掉了事件,那么mFirstTouchTarget就会执行子view,
// 执行玩之后直接break 跳出循环,无需再遍历了
// 保存该view到target链表
newTouchTarget = addTouchTarget(child, idBitsToAssign);
// 标记已经分发给子view,退出循环
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.
// 没有子view接收down事件,直接选择链表尾的view作为target
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// 如果不是ACTION_DOWN 的事件 将从以下开始处理
// Dispatch to touch targets.
// 首先判断 mFirstTouchTarget 是不是为null
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
// 如果找不到子view来处理事件,那么最后会交由view group来处理事件
// 如果 上面已经找到了一个子view来消耗事件了,那么这里的mFirstTouchTarget 不为空,就会接着往下执行
// 经过了前面的处理,到这里touchTarget依旧为null,说明没有找到处理down事件的子控件
// 或者down事件被viewGroup本身消费了,所以该事件由viewGroup自己处理
// 这里调用了dispatchTransformedTouchEvent方法来分发事件
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.
// 已经有子view 消费了down事件
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
// 遍历所有的TouchTarget 并把事件分发下去
while (target != null) {
final TouchTarget next = target.next;
// 如果 子view消耗了 ACTION_DOWN 事件和别的事件,那么 alreadyDispatchedToNewTouchTarget和newTouchTarget已经有值了
// 所以就直接置handled 为true 返回,
// 如果 alreadyDispatchedToNewTouchTarget和newTouchTarget 的值为null,那么就不是ACTION_DOWN事件,即是ACTION_MOVE,
// ACTION_UP等别的事件的话 就会调用 ***2 出代码,把事件分发给子view
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
// 表示事件已经在前面处理好了,无需重复处理
handled = true;
} else {
// 正常分发事件或者分发取消事件
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
// 调用了dispatchTransformedTouchEvent方法来分发事件
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
// ***2
// 表示事件在前面已经处理了,不需要重复处理
handled = true;
}
// 如果发送了取消事件,则移除该target
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.
// 如果接收到取消获取up事件,说明事件序列结束
// 直接删除所有TouchTarget
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);
// 如果仅仅只是一个POINTER_UP
// 清除对应触控点的触摸信息
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
/**
* Transforms a motion event into the coordinate space of a particular child view,
* filters out irrelevant pointer ids, and overrides its action if necessary.
* If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
*/
// ViewGroup中真正执行事件派发的关键方法是dispatchTransformedTouchEvent,该方法会完成关键的事件分发逻辑
// 该方法接收原MotionEvent事件,是否进行取消,目标子view,以及目标子view感兴趣的触控id
// 如果不是取消事件这个方法会把元MotionEvent 中的触控点信息拆分出目标view感兴趣的触控点信息
// 如果是取消事件则不需要拆分直接发送取消事件即可
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.
// oldPointerIdBits 表示现在所有触控id
// desiredPointerIdBits来自于该view所在的touchTarget,表示该view感兴趣的触控点id
// 因为desiredPointerIdBits有可能全是1,所以需要和oldPointerIdBits进行位与
// 得到真正可接受的触控点信息
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.
// 控件处于不一致的状态。正在接受事件序列却没有一个触控点id符合
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.
// 来自原始MotionEvent 的新的MotionEvent,只包含目标感兴趣的触控点
// 最终派发的是这个MotionEvent
final MotionEvent transformedEvent;
// 两者相等,表死该view接受所有的触控点的事件
// 这个时候transfromedEvent相当于原始MotionEvent的复制
if (newPointerIdBits == oldPointerIdBits) {
// 当目标控件不存在通过setScaleX()等方法进行的变换时
// 为了效率会将原市事件简单地进行控件位置与棍定量变换之后
// 发送给目标的dispatchTouchEvent()方法返回
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;
}
// 复制原始MotionEvent
transformedEvent = MotionEvent.obtain(event);
} else {
// 如果两者不等,说明需要对试驾进行拆分
// 只生成目标感兴趣的触控点的信息
// 这里返利事件包括了许该事件的类型,触控点索引等。
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
// 对Motion Event的坐标系,转换为目标控件的坐标系并进行分发
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
// 计算滚动量偏移
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
// 寻在scale等变换,需要进行矩阵变换
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
// 调用子view的方法进行分发
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
// 分发完毕,回收MotionEvent
transformedEvent.recycle();
return handled;
}