解析Activity的构成
当一个点击事件产生后,事件最先传递给Activity。写Activity时会调用setContentView()方法来加载布局
setContentView()方法的实现
调用了getWindow().setContentView(layoutResID)
getWindow()得到的是一个PhoneWindow(PhoneWindow是继承抽象类Window的)
从PhoneWindow的setContentView()方法中可以看出:PhoneWindow将DecorView(DecorView是PhoneWindow类的内部类,并且继承了FrameLayout)作为整个应用窗口的根 View,而这个 DecorView
又将屏幕划分为两个区域:一个是 TitleView,另一个是ContentView
解析事件分发机制
点击事件被封装成了一个类:MotionEvent。而当这个MotionEvent产生后,那么系统就会将这个MotionEvent传递给View的层级,MotionEvent在View中的层级传递过程就是点击事件分发。
事件分发的3个重要方法:
- dispatchTouchEvent(MotionEvent ev)—用来进行事件的分发。返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响(表示是否消耗当前事件)
- onInterceptTouchEvent(MotionEvent ev)—用来进行事件的拦截,在dispatchTouchEvent()中调用,需要注意的是View没有提供该方法。(如果当前View拦截了某个事件,则在同一个事件序列中该方法不会被再次调用)
- onTouchEvent(MotionEvent ev)—用来处理点击事件,在dispatchTouchEvent()方法中进行调用。(表示是否消耗当前事件,不消耗的话,则在同一个事件序列中,当前View无法再次接收到事件)
它们的关系可用如下伪代码表示:
/**
* 如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,
* 事件就会交给ViewGroup处理,他的onTouchEvent方法就会被调用;
* 不拦截就继续传递给它的子元素,如此反复直至事件被最终处理
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if(onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
源码分析
点击事件产生后,事件首先会传递给当前的 Activity,这会调用 Activity 的dispatchTouchEvent()方
法,当然具体的事件处理工作都是交由Activity中的PhoneWindow来完成的,然后PhoneWindow再把事件处
理工作交给DecorView,之后再由DecorView将事件处理工作交给根ViewGroup。
Activity的dispatchTouchEvent()
源码如下:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//onUserInteraction() 在Activity中为空方法。留给用户来重写的
//每当Key,Touch,Trackball事件分发到当前Activity就会被调用。如果你想当你的Activity在运行的时候,能够得知用户正在与你的设备交互,
//你可以override该方法。此回调和Activity.onUserLeaveHint为了帮助Activities智能的管理状态栏Notification;特别是为了帮助Activities在恰当的时间取消Notification。
//对活动的Activity.onUserLeaveHint回调的所有调用都将伴随对Activity.OnUserInteraction的调用。这可确保您的活动将被告知相关的用户活动,例如拉下通知窗格并触摸其中的项目。
//请注意,将针对开始触摸手势的触摸操作调用此回调,但可能不会针对随后的触摸移动和触摸操作调用此回调。
onUserInteraction();
//触摸屏幕点击home、back、menu键等都会触发此方法
}
//Window是一个抽象类,superDispatchTouchEvent()是抽象方法
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
//事件开始交给Activity所附属的Window进行分发
//如果返回true,整个事件循环就结束了
//返回false,表明事件没人处理,所有View的onTouchEvent都返回了false,那么Activity的onTouchEvent就会调用
}
return onTouchEvent(ev);
}
getWindow()
public Window getWindow() {
//mWindow = new PhoneWindow(this)
return mWindow;
}
PhoneWindow.superDispatchTouchEvent()
public boolean superDispatchTouchEvent(MotionEvent ev){
returen mDector.superDispatchTouchWindow(ev);
}
DecorView.superDispatchTouchEvent()
public boolean superDispatchTouchEvent(MotionEvent event){
return super.dispatchTouchEvent(event);
//调用父类ViewGroup的dispatchTouchEvent()
//即将事件传递给ViewGroup去处理
}
public final class DecorView extends FrameLayout implents RootViewSurfaceTracker{
private DecorView mDecor;
public final View getDecorView(){
if(mDecor == null){
inStallDecor();
}
return mDecor;
}
}
ViewGroup的dispatchTouchEvent()方法的源码分析
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 如果检查对象不空就检查事件(包括检查事件序列完整性),在View中也有这一步,但是传入的嵌套级别为0。
// 注释中有说如果事件已经被嵌套级别较高的View检查过了就不会再检查一遍了。
// 所以这里对事件已经进行过1等级的处理了,分发给下级View时就不会重新检查了
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.
// 如果事件目标是焦点View,并且当前view或它的子view可以获取焦点
// ev.isTargetAccessibilityFocus()判断事件是否具有焦点可访问性(是否可以获取焦点)(isFocusable isFocusableInTouchMode)
// 解释:Android在系统级别引入了辅助功能选项来帮助有障碍的用户使用系统,所以一个事件带有FLAG_TARGET_ACCESSIBILITY_FOCUS标志,说明这是一个特殊的辅助功能事件,需要进行特殊处理
// 焦点分为两种:
// a.硬键盘焦点focusable
// b.触摸焦点focusableInTouchMode(针对触屏情况下,点击屏幕的控件,先触发 OnFocusChangeListener,获取控件焦点,然后执行点击事件。EditText是默认有触摸获取焦点功能的,并将第一抢先获取焦点,因此页面有EditText的时默认有光标,键盘弹出)
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
// 设置不需要判断,取消FLAG_TARGET_ACCESSIBILITY_FOCUS标志,继续正常分发工作,以后就不需要重复判断了
ev.setTargetAccessibilityFocus(false);
}
// handled是返回的结果,表示是否被分发,默认当然是
boolean handled = false;
// onFilterTouchEventForSecurity()方法:
// 如果是视图窗口被遮挡且接收到运动事件的窗口是部分的或完全被遮挡(安全敏感程序 恶意应用程序),返回false;否则返回true(不过滤)
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
// 如果是ACTION_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.
// 由于一些特殊原因丢失ACTION_UP或者ACTION_CANCEL,导致事件序列结束时mFirstTouchTarget(TouchTarget链表)未被清空,新事件序列到达时,要先清空mFirstTouchTarget
// 在这里包含了诸如mFirstTouchTarget=null这样的初始化操作
// Cancels and clears all touch targets.
// private void cancelAndClearTouchTargets(MotionEvent event) {
// // mFirstTouchTarget != null
// // 当事件由ViewGroup的子元素成功处理时,mFirstTouchTarget会被赋值并指向子元素
// // 换种方式说,当ViewGroup不拦截事件并将事件交由子元素处理时mFirstTouchTarget != null
// // 如果上个事件序列中消耗事件的View的引用还没来得及回收
// if (mFirstTouchTarget != null) {
// boolean syntheticEvent = false;
// // 如果事件为空,就创建一个ACTION_CANCEL事件
// // 从disptachTouchEvent()中调用的此方法不会进入这个方法块,因为已经判断了为ACTION_DOWN
// if (event == null) {
// final long now = SystemClock.uptimeMillis();
// event = MotionEvent.obtain(now, now,
// MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
// // 设置事件源类型为触摸屏幕
// event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
// // 标记这是一个合成事件
// syntheticEvent = true;
// }
// // TouchTarget是一个链表结构,保存了事件传递的子一系列目标View
// for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
// // 检查View是否设置了暂时不在接收事件的PFLAG_CANCEL_NEXT_UP_EVENT标志位,如果有清除该标志位
// // 这样该View就能够接收下一次事件了
// resetCancelNextUpFlag(target.child);
// // 传递一个ACTION_CANCEL给所有链上的所有View
// // private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
// // View child, int desiredPointerIdBits) {
// // final boolean handled;
// // final int oldAction = event.getAction();
// // // 需要取消或是事件类型为ACTION_CANCEL
// // if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
// // // 传参需要取消,不管event是什么类型,都先设置一个ACTION_CANCEL
// // event.setAction(MotionEvent.ACTION_CANCEL);
// // // 如果没有子View了,就交给View来处理
// // if (child == null) {
// // handled = super.dispatchTouchEvent(event);
// // } else {
// // // 如果有就传递给子View继续分发
// // handled = child.dispatchTouchEvent(event);
// // }
// // // 再设置回原来的样子
// // event.setAction(oldAction);
// // return handled;
// // }
// // // ......
// // }
// dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
// }
// // 清空触摸事件的目标队列
// clearTouchTargets();
//
// if (syntheticEvent) {
// // 如果是合成事件,需要回收它
// event.recycle();
// }
// }
// }
cancelAndClearTouchTargets(ev);
//Resets all touch state in preparation for a new cycle.
// private void resetTouchState() {
// // 再清除一次事件传递链中的View
// clearTouchTargets();
// // 再次清除View中不接收TouchEvent的标志
// resetCancelNextUpFlag(this);
// // 撤销FLAG_DISALOW_INTERCEPT(设置为允许拦截事件)
// // 所以这个不允许拦截的标志位对ACTION_DOWN事件无效
// mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
// mNestedScrollAxes = SCROLL_AXIS_NONE;
// }
resetTouchState();
}
// Check for interception.
// 检查是否拦截(重点)
final boolean intercepted;
// ViewGroup会在如下两种情况下判断是否要拦截当前事件
// 如果是ACTION_DOWN或者事件序列的前几个事件已经有子View消耗了
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 按位与,两个都为1结果为1
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
// 没有设置不允许拦截才去判断是否拦截
if (!disallowIntercept) {
// 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;
// }
// 当ViewGroup决定拦截事件后,那么后续的点击事件将会默认交给它处理并且不再调用它的onInterceptTouchEvent方法
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.
// 没有被子View消耗而且不是ACTION_DOWN事件
// 会有两种情况:
// 1.一开始ViewGroup就拦截了ACTION_DOWN事件
// 2.或者子View没有消耗前几个事件,它们对于之后的事件也不感兴趣,所以拦截
// mFirstTouchTarget为null,那么ViewGroup就默认拦截接下来同一序列中的所有点击事件
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消耗该事件,就开始普通的事件分发
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
// 如果设置了FLAG_CANCEL_NEXT_UP_EVENT就取消这个设置或者该事件是cancel类型
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
// 是否拆分事件,默认可拆分
// 在必要情况下,可以将一个事件分解传递给多个子View
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
//如果事件没有取消且不拦截(重点)
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.
// 如果事件的目标是可访问性焦点,我们将其提供给具有辅助功能焦点的视图,
// 如果它不处理它我们像往常一样,把flag清除,并把活动分派给所有的孩子。我们正在查找以可访问性为中心的主机,以避免陈述,因为这些事件非常罕见。
//
// private View findChildWithAccessibilityFocus() {
// ViewRootImpl viewRoot = getViewRootImpl();
// if (viewRoot == null) {
// return null;
// }
//
// View current = viewRoot.getAccessibilityFocusedHost();
// if (current == null) {
// return null;
// }
// // 一层一层向上找,直到找到一个View为该ViewGroup的child并且还嵌套着焦点View
// ViewParent parent = current.getParent();
// while (parent instanceof View) {
// if (parent == this) {
// return current;
// }
// current = (View) parent;
// parent = current.getParent();
// }
//
// return null;
// }
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
// 如果是ACTION_DOWN
// 或者是可拆分且已经有一个触摸点,用户又使用一个手指触摸屏幕
// 或者是鼠标移动但未点击
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
// 如果可拆分就把通过索引获取的值左移一位(通过getPointerId()获得ID,这样就可以在多个motion event中追踪pointer)
// 否则取常量-1
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.
// 清除这个id早期的触摸目标
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.
// /**
// * Provide custom ordering of views in which the touch will be dispatched.
// */
// public ArrayList<View> buildTouchDispatchChildList() {
// return buildOrderedChildList();
// }
//
// ArrayList<View> buildOrderedChildList() {
// final int childrenCount = mChildrenCount;
// // 如果子View数小于等于1
// // 或者遍历子View不存在getZ()不为0的View(z属性用于描述视图距离它父视图的高度)
// if (childrenCount <= 1 || !hasChildWithZ()) return null;
//
// if (mPreSortedChildren == null) {
// mPreSortedChildren = new ArrayList<>(childrenCount);
// } else {
// // callers should clear, so clear shouldn't be necessary, but for safety...
// mPreSortedChildren.clear();
// // 重新分配大小
// mPreSortedChildren.ensureCapacity(childrenCount);
// }
//
// final boolean customOrder = isChildrenDrawingOrderEnabled();
// // 开始遍历子View
// for (int i = 0; i < childrenCount; i++) {
// // add next child (in child order) to end of list
// // getAndVerifyPreorderedIndex():是否设置有FLAG_USE_CHILD_DRAWING_ORDER
// // 如果设置了,绘制的方法就会通过调用getChildDrawingOrder()来决定绘制子View的顺序。
// final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
// // 获得这个View
// final View nextChild = mChildren[childIndex];
// final float currentZ = nextChild.getZ();
//
// // insert ahead of any Views with greater Z
// int insertIndex = i;
// // 向数组前面遍历查找是否有深度大于当前子View的
// while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
// insertIndex--;
// }
// // 将当前子View插入到这个位置,使集合按getZ()从小到大排序
// mPreSortedChildren.add(insertIndex, nextChild);
// }
// // 将排好序的集合返回
// return mPreSortedChildren;
// }
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// 从后往前
// 判断子元素是否能够接收到点击事件,有两点:
// 1.子元素是否在播动画
// 2.点击事件的坐标是否落在子元素的区域内
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
// preorderedList不空就从它里面取出第childIndex个View
// 空的话从children中取
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.
// 该viewgroup设置事件了指向焦点View并且焦点View在前面已经找到了
if (childWithAccessibilityFocus != null) {
// 判断遍历的此View是否是焦点View ,如果不是直接下一遍循环
if (childWithAccessibilityFocus != child) {
continue;
}
// 如果是的话就将找到的焦点view置空
// i回到到数第一个下标
// 这样做的目的是先让该焦点view尝试进行下面的普通分发操作
// 如果成功了,会在下面跳出循环。
// 如果不成功,就将记录的焦点view置空,
// 从最后一个开始重新遍历,不再进入这个判断。
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
// 如果不可见并且没有动画 或者 事件不在该View范围内
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
// 设置false,直接跳出进入下一次循环
ev.setTargetAccessibilityFocus(false);
continue;
}
// private TouchTarget getTouchTarget(@NonNull View child) {
// for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
// if (target.child == child) {
// return target;
// }
// }
// return null;
// }
newTouchTarget = getTouchTarget(child);
// 如果存在,说明这个view之前处理过事件,再给它这个事件并跳出循环。
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;
}
// 取消该view的PFLAG_CANCEL_NEXT_UP_EVENT
resetCancelNextUpFlag(child);
// 将事件传递给child看是否能分发消耗成功,如果成功
// dispatchTransformedTouchEvent实际上调用的就是子元素的dispatchTouchEvent方法
// 它的内部有如下一段内容:
// if (child == null) {
// handled = super.dispatchTouchEvent(event);
// } else {
// handled = child.dispatchTouchEvent(event);
// }
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]) {
// 找到该view的原始下标
mLastTouchDownIndex = j;
break;
}
}
} else {
// 如果集合为空,也就说明child是根据原始下标取出的,直接赋值
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
// 头插,赋值成这个view的touchtarget,并且会把mFirstTouchTarget也改成该值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
// 置为true表示已经分发给新目标了
alreadyDispatchedToNewTouchTarget = true;
// 跳出循环
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
// 焦点View没有成功处理事件所以将flag置为false,以后就普通的去遍历分发了
ev.setTargetAccessibilityFocus(false);
}
// 清空排序集合
if (preorderedList != null) preorderedList.clear();
}
// 没有进入上面的if判断,说明该ViewGroup的子View数量为0
// 没有子View并且之前消耗过事件
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;
// 找到消耗链表的最后一个元素,也就是第一个被添加的元素,down事件消耗的view
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
// 给这个目标加上这个事件的id
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
// 如果事件序列中还没有分发消耗的事件对象
// 两种情况:
// 1.ViewGroup没有子元素
// 2.子元素处理了点击事件,但在dispatchTouchEvent中返回了false(一般是因为子元素在onTouchEvent中返回了false
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;
// 遍历到消耗事件的子View,置handled为true
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
// 如果设置了PFLAG_CANCEL_NEXT_UP_EVENT就取消或需要拦截 返回true
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
// 如果cancelChild为true,就让该child分发一个cancel事件
// 如果cancelChild为false,就让该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;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// 如果是取消或up事件或鼠标移动事件,就清空所有状态
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;
}
mFirstTouchTarget
当事件由ViewGroup的子元素成功处理时,mFirstTouchTarget会被赋值并指向子元素 换种方式说,当ViewGroup不拦截事件并将事件交由子元素处理时mFirstTouchTarget != null
FLAG_DISALLOW_INTERCEPT
这个标记位是通过requestDisallowInterceptTouchEvent方法来设置的(一般用于子View中)
FLAG_DISALLOW_INTERCEPT一旦设置后,ViewGroup将无法拦截除了ACTION_DOWN以外的其他点击事件。
ViewGroup在分发事件时,如果是ACTION_DOWN就会重置FLAG_DISALLOW_INTERCEPT,将导致子View中设置的这个标记位无效
当面对ACTION_DOWN事件时,ViewGroup总会调用自己的onInterceptTouchEvent方法来询问自己是否要拦截事件
TouchTarget
这个类应该是把一个事件流(从down到up或cancel等)以链表的形式存储下来
流程图