前一篇文章:浅析了事件拦截机制
主要是从demo中看的现象总结的结论
文中涉及到以下方法
1. ViewGroup的三个方法:
dispatchTouchEvent:事件分发
onInterceptTouchEvent:事件拦截
onTouchEvent:事件触发
- View的两个方法
dispatchTouchEvent:事件分发
onTouchEvent:事件触发
而且当时我们忽略了一个方法dispatchTouchEvent
其实dispatchTouchEvent这个方法其实才是处理事件分发拦截的主要方法
今天我们重点解析此方法。
Activity中的dispatchTouchEvent
当我们点击屏幕时候,事件到底怎么传递的呢
其实在Activity中也有dispatchTouchEvent方法,我们来看源码:
Activity:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
onUserInteraction();其实是一个空方法,我们忽略。
如果getWindow().superDispatchTouchEvent(ev)方法返回的是true,则返回true,否则执行Activity的onTouchEvent(ev);
我们再看如果getWindow().superDispatchTouchEvent(ev)方法,其实是用的抽象类Window中的superDispatchTouchEvent方法,最终实现类是PhoneWindow,我们再看PhoneWindow的superDispatchTouchEvent方法:
PhoneWindow:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
mDecor是一个DecorView,它继承了FrameLayout,其实就是Activity的根view。
DecorView:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
我们看到其实最终触发的是ViewGroup的dispatchTouchEvent。
小结:activity中事件传递方向,如下图:
graph TD
A(Activity)-->|dispatchTouchEvent|B(PhoneWindow)
B-->|superDispatchTouchEvent|C(DecorView)
C-->|superDispatchTouchEvent|D{
ViewGroup}
D-->|dispatchTouchEvent|E{
ViewGroup}
D-->|dispatchTouchEvent|F(View)
E-->|dispatchTouchEvent|G(ViewGroup)
E-->|dispatchTouchEvent|K(View)
我们看到,最终是由ViewGroup来处理事件的。下面我们重点分析ViewGroup的dispatchTouchEvent方法。
ViewGroup和View中的dispatchTouchEvent
先看一下伪代码:
ViewGroup:
public boolean dispatchTouchEvent(MotionEvent event) {
if(onInterceptTouchEvent(event)){
//是否拦截
return onTouchEvent(event);
}
//没有拦截
if(child==null){
//没有子控件
return onTouchEvent(event);
}else{
//执行子控件的dispatchTouchEvent
boolean consume= child.dispatchTouchEvent(event);
if(!consume){
//子控件没有消费事件,执行当前view的onTouchEvent
return onTouchEvent(event);
}else{
return false;
}
}
}
ViewGroup中:
看到:
如果onInterceptTouchEvent返回true,则执行当前ViewGroup的onTouchEvent。
如果onInterceptTouchEvent返回false: 如果有子控件,则向下传递事件,执行子控件的dispatchTouchEvent,如果没有子控件,则执行当前ViewGroup的onTouchEvent。
如果子控件的onInterceptTouchEvent返回false,则说明没有消费,当前控件执行onTouchEvent,否则返回false,表示所有子控件都没有消费事件,向上传递。
如果最上层控件也没有消费事件,则最终交给activity执行onTouchEvent事件。
View中:
先看下从ViewGroup中事件向下传递的逻辑:
//向下传递事件,执行子控件的dispatchTouchEvent
boolean consume= child.dispatchTouchEvent(event);
if(!consume){
//子控件没有消费事件,执行当前view的onTouchEvent
return onTouchEvent(event);
}else{
return false;
}
再看View中的dispatchTouchEvent,伪代码如下:
View:
public boolean dispatchTouchEvent(MotionEvent event) {
return onTouchEvent(event);
}
从上面代码看到,如果child如果是View,则会返回其onTouchEvent(event)方法的结果。当然View中还有
还有onTouchListener onClickListener等事件的触发。这些方法我们后面再细细分析。现在我们暂时只关注onTouchEvent方法。
该方法如果返回true,则表示view消费了事件,其父控件的onTouchEvent方法将不会触发。
如果返回false,父控件则触发onTouchEvent方法并返回onTouchEvent方法的结果。
dispatchTouchEvent源码分析
public boolean dispatchTouchEvent(MotionEvent ev) {
//验证事件是否连续
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
//这个变量用于记录事件是否被处理完
boolean handled = false;
//过滤掉一些不合法的事件:当前的View的窗口被遮挡了。
if (onFilterTouchEventForSecurity(ev)) {
//如果事件发生的View在的窗口,没有被遮挡
final int action = ev.getAction();
//重置前面为0 ,只留下后八位,用于判断相等时候,可以提高性能。
final int actionMasked = action & MotionEvent.ACTION_MASK;
//判断是不是Down事件,如果是的话,就要做初始化操作
if (actionMasked == MotionEvent.ACTION_DOWN) {
//如果是down事件,就要清空掉之前的状态,比如,重置手势判断什么的。
//比如,之前正在判断是不是一个单点的滑动,但是第二个down来了,就表示,不可能是单点的滑动,要重新开始判断触摸的手势
//清空掉mFirstTouchTarget
// 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.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
//检查是否拦截事件
final boolean intercepted;
//如果当前是Down事件,或者已经有处理Touch事件的目标了
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//判断允不允许这个View拦截
//使用与运算作为判断,可以让我们在flag中,存储好几个标志
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//如果说允许拦截事件
if (!disallowIntercept) {
//确定是不是拦截了
intercepted = onInterceptTouchEvent(ev);
//重新恢复Action,以免action在上面的步骤被人为地改变了
ev.setAction(action);