}
abstract class InputStage {
private final InputStage mNext;
public InputStage(InputStage next) {
mNext = next;
}
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
traceEvent(q, Trace.TRACE_TAG_VIEW);
final int result;
try {
result = onProcess(q);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
apply(q, result);
}
}
}
如上所示:
1.QueuedInputEvent是一种输入事件,链表结构,遍历传递给InputStage
2.InputStage是处理输入的责任链,在调用deliver时会遍历责任链传递事件
3.事件分发完成后会调用finishInputEvent,告知SystemServer进程的InputDispatcher线程,最终将该事件移除,完成此次事件的分发消费。
那么问题来了,InputStage的责任链是什么时候组件的呢?
[](()2.3 组装责任链
我们得回到ViewRootImpl.setView方法中
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
…
// Set up the input pipeline.
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
“aq:native-post-ime:” + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
“aq:ime:” + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
“aq:native-pre-ime:” + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
…
}
}
可以看到在setView方法中,就把这条输入事件处理的责任链拼接完成了,不同的InputStage子类,通过构造方法一个个串联起来了,那这些InputStage到底干了啥呢?
-
SyntheticInputStage。综合处理事件阶段,比如处理导航面板、操作杆等事件。 -
ViewPostImeInputStage。视图输入处理阶段,比如按键、手指触摸等运动事件,我们熟知的view事件分发就发生在这个阶段。 -
NativePostImeInputStage。本地方法处理阶段,主要构建了可延迟的队列。 -
EarlyPostImeInputStage。输入法早期处理阶段。 -
ImeInputStage。输入法事件处理阶段,处理输入法字符。 -
ViewPreImeInputStage。视图预处理输入法事件阶段,调用视图view的dispatchKeyEventPreIme方法。 -
NativePreImeInputStage。本地方法预处理输入法事件阶段。
小结一下,事件到达应用端的主线程,会通过ViewRootImpl进行一系列InputStage来处理事件。这个阶段其实是对事件进行一些简单的分类处理,比如视图输入事件,输入法事件,导航面板事件等等。
我们的View触摸事件就发生在ViewPostImeInputStage阶段
final class ViewPostImeInputStage extends InputStage {
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
}
}
}
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
boolean handled = mView.dispatchPointerEvent(event)
return handled ? FINISH_HANDLED : FORWARD;
}
//View.java
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
1.经过层层回调会调用到mView.dispatchPointerEvent
2.我们知道ViewRootImpl中的mView就是DecorView
现在事件已经传递到了DecorView,也就是我们界面的根布局
接下来是事件在Activity,Window,DecorView中的传递
[](()2.4 事件在Activity,Window,DecorView中的传递
//DecorView.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//cb其实就是对应的Activity/Dialog
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
//Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
//PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
//DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
可以看到事件分发经过了:DecorView -> Activity -> PhoneWindow -> DecorView
看起来是一个很奇怪的事件流转,事件从DecorView出发,最后又回到了DecorView,为什么这样做呢?
[](()2.4.1 为什么ViewRootImpl不直接把事件交给Activity?
主要是为了解藕
ViewRootImpl并不知道有Activity这种东西存在!它只是持有了DecorView。所以,不能直接把触摸事件送到Activity.dispatchTouchEvent()
[](()2.4.2 交给Acitivity后,为什么不直接交给DecorView开始分发事件呢?
因为Activity不知道有DecorView!
但是,Activity持有PhoneWindow ,而PhoneWindow当然知道自己的窗口里有些什么了,所以能够把事件派发给DecorView。
在Android中,Activity并不知道自己的Window中有些什么,这样耦合性就很低了,Activity不需要知道Window中的具体内容
[](()2.5 小结
经过上述过程,事件终于到了我们熟悉的ViewGroup.dispatchTouchEvent
流程图如下所示:

[](()3.Touch事件到达页面后内部怎样分发
下面就是我们最常用也是最常见的事件分发部分了
[](()3.1 ViewGroup是否拦截事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final boolean intercepted;
//只有ActionDown或者mFirstTouchTarget为空时才会判断是否拦截
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
}
}
if (!canceled && !intercepted) {
//事件传递给子view
…
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
…
//如果子View消耗了则给mFirstTouchTarget赋值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
…
}
}
//mFirstTouchTarget不为空时会调用dispatchTransformendTouchEvent
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
}
private boolean dispatchTransformedTouchEvent(View child) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
}
从以上可以看出
1.只有当Action_Down或者mFirstTouchTarget不为空时才判断是否拦截
2.mFirstTouchTarget是个链表结构,代表某个子View消费了事件,为null则表示没有子View消费事件
3.在判断是否拦截前有个disallowIntercept字段,这个在后面事件冲突内部拦截法时会用到
4.接下来就到了onInterceptTouchEvent,ViewGroup是否拦截事件正是由这个方法控制的
[](()3.1.2 ViewGroup拦截后会发生什么?
1.拦截之后,事件自然就不会再下发给子View
2.接下来如果mFirstTouchTarget为null,则会调用到dispatchTransformedTouchEvent,然后调用到super.dispatchTouchEvent,最终到ViewGroup.onTouchEvent
3.为什么使用mFirstTouchTarget==null来判断是否是ViewGroup处理,是因为mFirstTouchTarget==null有两种情况,一是ViewGroup拦截,二是子View没有处理事件,两种情况最后都回调到ViewGroup.onTouchEvent
通过上面的分析,我们可以得出ViewGroup拦截的伪代码:
public boolean dispatchTouchEvent(MotionEvent event) {
boolean isConsume = false;
if (isViewGroup) {
if (onInterceptTouchEvent(event)) {
isConsume = super.dispatchTouchEvent(event);
}
}
return isConsume;
}
如果是ViewGroup,会先执行到onInterceptTouchEvent方法判断是否拦截,如果拦截,则执行父类View的dispatchTouchEvent方法。
[](()3.1.3 ViewGroup不拦截会发生什么?
如果ViewGroup不拦截,则会传递到子View
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int childrenCount = mChildrenCount;
//遍历子View
if (newTouchTarget == null && childrenCount != 0) {
for (int i = childrenCount - 1; i >= 0; i–) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
//2.判断事件坐标
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
//3.传递事件
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
}
}
private boolean dispatchTransformedTouchEvent(View child) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
}
如果不拦截,ViewGroup内主要做以下几件事
1.遍历当前ViewGr **《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》无偿开源 徽信搜索公众号【编程进阶路】** oup的所有子View
2.判断当前View是否在当前子View的坐标范围内,不在范围内不能接收事件,直接跳过
3.利用dispatchTransformedTouchEvent,如果返回true,则通过addTouchTarget对mFirstTouchTarget赋值
4.dispatchTransformedTouchEvent做的主要就是两个事,如果child不为null,则事件分发到child,否则调用super.dispatchTouchEvent,并最终返回结果
5.mFirstTouchTarget是单链表结构,记录消费链,但是在单点触控的时候这个特性没有用上,只是一个普通的TouchTarget对象
[](()3.2 子View是否拦截
public boolean dispatchTouchEvent(MotionEvent event) {
if (onFilterTouchEventForSecurity(event)) {
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}
子View的diapatchTouchEvent逻辑比较简单
1.如果设置了setOnTouchListener并且返回为true,那么onTouchEvent就不再执行
2.否则执行onTouchEvent,我们常用的OnClickListenr就是在onTouchEvent里触发的
所以默认情况下会直接执行onTouchEvent,如果我们设置了setOnClickListener或者setLongClickListener,都会正常触发
[](()3.2.1 如果子View消费事件会怎么样?
上面说了,如果子View消费事件,即dispatchTouchEvent方法返回true
表示这个事件我处理了,那么事件从此结束,ViewGroup的dispatchTouchEvent也返回true
最后回到Activity的dispatchTouchEvent,也是直接返回true
//Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
小结:如果子View消费事件的话,事件就此结束了
[](()3.2.2 如果子View不消费事件会怎么样?
子View不拦截事件,那么mFirstTouchTarget就为null,退出循环后,调用了dispatchTransformedTouchEvent方法。
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
小结一下:
1.子View不拦截事件,就回调到了dispatchTransformedTouchEvent
2.然后就调到了super.dispatchTouchEvent
3.那么接下来ViewGroup就跟子View的逻辑一样了,默认执行onTouchEvent,如果设置了setOnTouchLister则执行onTouch
[](()3.3 如果ViewGroup与子View都不拦截会怎么样
如果ViewGroup与子View都不拦截,即mFirstTouchTarget == null,dispatchTouchEvent也返回false
再看看Activity的源码
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
答案很明显:会执行Activity的onTouchEvent方法
[](()3.4 后续事件如何分发?
事件分发的处理者已经找到了,看起来任务已经完成了。
但其实事件分发是包括ACTION_DOWN、ACTION_MOVE、ACTION_UP、ACTION_CANCEL的一系列事件,我们上面分析的都是Action_DOWN的过程
后续事件如何处理?
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!canceled && !intercepted) {










