window的最底层的View就是DecorView,那么这个时候调用的应该就是DecorView的dispatchTouchEvent方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
callBack是谁?,在Activity的attach方法中,
mWindow.setCallback(this);
这个callback就是activity本身,所以我们要去Activity中查看它的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
也就是activity会先将事件交给DecorView去处理,如果被消耗掉,就返回true。如果没有消耗这个事件,就回调Activity自己的onTouchEvent。
总结
那么把上面的一大坨我们简略的来讲如下的流程:
用户触摸屏幕产生设备节点中断并保存到/dev/input/目录下
底层的EventHub监听目录,将事件读出并加工返回给随着WMS一起启动的底层的InputReader
InputReader处理加工之后交给InputDispatcher来进行分发,通过socket通知UI进程的InputEventReceiver接收到事件
InputEventReceiver将回调事件一步步传递给Activity来进行分发
Activity先将事件交给DecorView来进行处理,如果DecorView消耗则返回true,否则自己回调onTouchEvent方法
View中dispatchTouchEvent分析
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;
}
以上代码可看出四个条件只要有一个不满足就会调用onTouchEvent事件
结论
onTouch优先于onTouchEvent执行。
如果在onTouch方法中通过返回true将事件消费掉,onTouchEvent将不会再执行。
onTouch能够得到执行需要两个前提条件
- mOnTouchListener的值不能为空。
- 当前点击的控件必须是enable的。
因此如果你有一个控件是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,就必须通过在该控件中重写onTouchEvent方法来实现。
ViewGroup
首先判断是否拦截
如果是按下事件,在允许拦截的情况下,调用onInterceptTouchEvent判断是否拦截
如果不是按下事件,直接拦截
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
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;
}
拦截
然后根据按下的坐标位置筛选出可以接受该点击事件的子View,并按照Z轴进行排序
源码中的英文为
// Find a child that can receive the event.
// Scan children from front to back.
然后遍历这些筛选出来的子View
调用
dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
不拦截
调用
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
在dispatchTransformedTouchEvent函数中,如果子View(child)不为null,调用子View的dispatchTouchEvent
否则就调用自身View的dispatchTouchEvent
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
......
}