Android事件分发机制(1)——从源码角度分析

前言

这两天在写一个自定义控件,继承了ViewGroup,需要对子View做一个事件分派的控制,之前用的onTouchEvent事件比较多,对于android的事件分发体系很早之前有了解过,很久没用就忘记了,今天在这里记录总结下。

首先放上一张事件分发流程图(之前摘自网上的一篇博客,忘了哪一篇了)

从上图我们可以看到事件的分发流程是从 Activity(Window)——>ViewGroup——>View,主要有三个关键函数:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent

解析

事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

首先用Activity捕获到用户的触摸事件,Activity本身并没有拦截事件,所以当Activity捕获到事件后将其传递到最外层的View的dispatchTouchEvent方法,t进行事件分发,返回值说明:

1)return true:当前View消费所有的事件,后续的move和up事件都交给自己处理;

2)return false:停止分发,将事件交给上层的View的onTouchEvent进行处理,

3)调用super的dispatchTouchEvent:将事件交给本View的onInterceptTouchEvent进行处理

事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

用于拦截事件,决定事件的传递方向,此事件是在ViewGroup中定义的,子View没有此事件

1)return true: 拦截该事件,将事件交给自身的onTouchEvent处理;

2)return false\super.onInterceptTouchEvent:不拦截,将事件下派给子View ,由子View的dispatchTouchEvent进行事件处理


事件响应:public boolean onTouchEvent(MotionEvent ev)

1)return true:表示View消费了此事件,后续的move和up都交给该view处理

2)return false\super:不消费此事件,将事件层层递交给父级的onTouchEvent处理,直到某一父级return true,否则最终交给Activity处理:

实例

1)、不消费,不拦截:下面通过实例来看看事件的传递过程,新建一个Group1类,继承自LinearLayout,分别重新上面三个方法:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.d("Dispatch","Group 1 dispatchTouchEvent eventType: " + ACTIONS[ev.getAction()]);
    return super.dispatchTouchEvent(ev);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.d("Dispatch","Group 1 onInterceptTouchEvent eventType: " + ACTIONS[ev.getAction()]);
    return super.onInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d("Dispatch","Group 1 onTouchEvent eventType: " + ACTIONS[event.getAction()]);
    return super.onTouchEvent(event);
}

然后新建一个View1类,继承自TextView,重写dispatchTouchEvent和onTouchEvent:

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.d("Dispatch","View1 1 dispatchTouchEvent eventType: " + ACTIONS[event.getAction(]));
    return super.dispatchTouchEvent(event);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d("Dispatch","View1 1 onTouchEvent eventType: " + ACTIONS[event.getAction()]);
    return super.onTouchEvent(event);
}

然后在MainActivity重写dispatchTouchEvent和onTouchEvent:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.d("Dispatch","MainActivity dispatchTouchEvent eventType: " + ACTIONS[ev.getAction()]);
    return super.dispatchTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d("Dispatch","MainActivity onTouchEvent eventType: " + ACTIONS[event.getAction()]);
    return super.onTouchEvent(event);
}

运行项目,得到打印的信息为:

MainActivity dispatchTouchEvent eventType: ACTION_DOWN
Group 1 dispatchTouchEvent eventType: ACTION_DOWN
Group 1 onInterceptTouchEvent eventType: ACTION_DOWN
View1 1 dispatchTouchEvent eventType: ACTION_DOWN
View1 1 onTouchEvent eventType: ACTION_DOWN
Group 1 onTouchEvent eventType: ACTION_DOWN
MainActivity onTouchEvent eventType: ACTION_DOWN
MainActivity dispatchTouchEvent eventType: ACTION_MOVE
MainActivity onTouchEvent eventType: ACTION_MOVE
MainActivity dispatchTouchEvent eventType: ACTION_UP
MainActivity onTouchEvent eventType: ACTION_UP

通过结果可以知道,由于在各个环节中,没有任何一个view拦截或者消费了事件,所以刚开始的Down事件由Activity.dispatchTouchEvent——>Group1.dispatchTouchEvent——>Group1.onInterceptTouchEvent——>View1.dispatchTouchEvent——>View1.onTouchEvent——>Group1.onTouchEvent——>Activity.onTouchEvent,而后续因为在各个子View都没有消费此事件,后续的move和up事件都由Activity处理

2)、我们在Activity的dispatchTouchEvent返回true\false,看看结果怎样:

MainActivity dispatchTouchEvent eventType: ACTION_DOWN
MainActivity dispatchTouchEvent eventType: ACTION_MOVE
MainActivity dispatchTouchEvent eventType: ACTION_UP

Activity直接消费了此事件并且往下传(包括自身的onTouchEvent),

3)修改Group1的dispatchTouchEvent返回true,结果:

MainActivity dispatchTouchEvent eventType: ACTION_DOWN
Group 1 dispatchTouchEvent eventType: ACTION_DOWN
MainActivity dispatchTouchEvent eventType: ACTION_MOVE
Group 1 dispatchTouchEvent eventType: ACTION_MOVE
MainActivity dispatchTouchEvent eventType: ACTION_UP
Group 1 dispatchTouchEvent eventType: ACTION_UP

在Group1的事件分派函数返回true,相当于该函数消费了该事件,并截断了事件的分派流程,后续的move和up都交给该View的dispatchTouchEvent处理

4)修改Group1的dispatchTouchEvent返回false,结果:

MainActivity dispatchTouchEvent eventType: ACTION_DOWN
Group 1 dispatchTouchEvent eventType: ACTION_DOWN
MainActivity onTouchEvent eventType: ACTION_DOWN
MainActivity dispatchTouchEvent eventType: ACTION_MOVE
MainActivity onTouchEvent eventType: ACTION_MOVE
MainActivity dispatchTouchEvent eventType: ACTION_MOVE
MainActivity onTouchEvent eventType: ACTION_MOVE
MainActivity dispatchTouchEvent eventType: ACTION_UP
MainActivity onTouchEvent eventType: ACTION_UP
在Group1的事件分派函数返回false,事件没有往下分发,也没有消费,最终回到Activity的onTouchEvent处理

5)修改Group1的onInterceptTouchEvent返回true,结果:

MainActivity dispatchTouchEvent eventType: ACTION_DOWN
Group 1 dispatchTouchEvent eventType: ACTION_DOWN
Group 1 onInterceptTouchEvent eventType: ACTION_DOWN
Group 1 onTouchEvent eventType: ACTION_DOWN
MainActivity onTouchEvent eventType: ACTION_DOWN
MainActivity dispatchTouchEvent eventType: ACTION_MOVE
MainActivity onTouchEvent eventType: ACTION_MOVE
MainActivity dispatchTouchEvent eventType: ACTION_UP
MainActivity onTouchEvent eventType: ACTION_UP

由于在Group1的拦截函数中返回true,即是拦截了该事件,不再往下 派送,然后down事件转向Group1的onTouchEvent处理,因为Group1的onTouchEvent没有消费该事件,继续往上传给Activity的onTouchEvent,最后后续的move和up也由该事件处理。

6)修改Group1的onInterceptTouchEvent返回false,结果:

MainActivity dispatchTouchEvent eventType: ACTION_DOWN
Group 1 dispatchTouchEvent eventType: ACTION_DOWN
Group 1 onInterceptTouchEvent eventType: ACTION_DOWN
View1 1 dispatchTouchEvent eventType: ACTION_DOWN
View1 1 onTouchEvent eventType: ACTION_DOWN
Group 1 onTouchEvent eventType: ACTION_DOWN
MainActivity onTouchEvent eventType: ACTION_DOWN
MainActivity dispatchTouchEvent eventType: ACTION_UP
MainActivity onTouchEvent eventType: ACTION_UP

Down事件完整的传递了下来,又没有子View消费down事件,后续的事件交回给Activity处理

7)修改View1的Group1的onTouchEvent为true同时Group1的onInterceptTouchEvent返回true

MainActivity dispatchTouchEvent eventType: ACTION_DOWN
Group 1 dispatchTouchEvent eventType: ACTION_DOWN
Group 1 onInterceptTouchEvent eventType: ACTION_DOWN
Group 1 onTouchEvent eventType: ACTION_DOWN
MainActivity dispatchTouchEvent eventType: ACTION_MOVE
Group 1 dispatchTouchEvent eventType: ACTION_MOVE
Group 1 onTouchEvent eventType: ACTION_MOVE
MainActivity dispatchTouchEvent eventType: ACTION_MOVE
Group 1 dispatchTouchEvent eventType: ACTION_MOVE
Group 1 onTouchEvent eventType: ACTION_MOVE
MainActivity dispatchTouchEvent eventType: ACTION_UP
Group 1 dispatchTouchEvent eventType: ACTION_UP
Group 1 onTouchEvent eventType: ACTION_UP

由于Group1拦截了down事件,所以事件不再往下传,在Group1的onTouchEvent消费了事件,所以事件也不再往上传,在这里有一点要注意,在第一次Group1拦截了down事件后,后续不会再调用onInterceptTouchEvent事件

view1的dispatchTouchEvent和onTouchEvent就不看了,基本上和Group1的类同。

此外,Android还为我们提供了onClick和onTouch的监听,在Activity里给View1加click监听:

findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.d("Dispatch","View1 onClick");
    }
});
findViewById(R.id.tv).setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.d("Dispatch","View1 onTouch eventType: " + ACTIONS[event.getAction()]);
        return false;
    }
});

运行结果:

MainActivity dispatchTouchEvent  : ACTION_DOWN
Group 1 dispatchTouchEvent eventType: ACTION_DOWN
Group 1 onInterceptTouchEvent eventType: ACTION_DOW
View1 1 dispatchTouchEvent eventType: ACTION_DOWN
View1 onTouch eventType: ACTION_DOWN
View1 1 onTouchEvent eventType: ACTION_DOWN
MainActivity dispatchTouchEvent  : ACTION_MOVE
Group 1 dispatchTouchEvent eventType: ACTION_MOVE
Group 1 onInterceptTouchEvent eventType: ACTION_MOV
View1 1 dispatchTouchEvent eventType: ACTION_MOVE
View1 onTouch eventType: ACTION_MOVE
View1 1 onTouchEvent eventType: ACTION_MOVE
MainActivity dispatchTouchEvent  : ACTION_UP
Group 1 dispatchTouchEvent eventType: ACTION_UP
Group 1 onInterceptTouchEvent eventType: ACTION_UP
View1 1 dispatchTouchEvent eventType: ACTION_UP
View1 onTouch eventType: ACTION_UP
View1 1 onTouchEvent eventType: ACTION_UP
View1 onClick

我们看到,onTouch事件是在onTouchEvent之前调用的,而onClick事件是在onTouch中被调用的,且是up事件执行完毕才触发的,最后的事件由onClick消费掉了,也就是说最终事件被View1消费了。接着,我们修改onTouch事件返回true,让它消费事件:

 
 
MainActivity dispatchTouchEvent  : ACTION_DOWN
Group 1 dispatchTouchEvent eventType: ACTION_DOWN
Group 1 onInterceptTouchEvent eventType: ACTION_DOWN
View1 1 dispatchTouchEvent eventType: ACTION_DOWN
View1 onTouch eventType: ACTION_DOWN
MainActivity dispatchTouchEvent  : ACTION_MOVE
Group 1 dispatchTouchEvent eventType: ACTION_MOVE
Group 1 onInterceptTouchEvent eventType: ACTION_MOVE
View1 1 dispatchTouchEvent eventType: ACTION_MOVE
View1 onTouch eventType: ACTION_MOVE
MainActivity dispatchTouchEvent  : ACTION_UP
Group 1 dispatchTouchEvent eventType: ACTION_UP
Group 1 onInterceptTouchEvent eventType: ACTION_UP
View1 1 dispatchTouchEvent eventType: ACTION_UP
View1 onTouch eventType: ACTION_UP
和上面想比,View1的onTouchEvent和onClick事件都没执行,这是因为onTouch消费了该事件,事件不再往下传,所以onTouchEvent和onClick事件都不会触发,在View1的onTouchEvent消费事件也是同理,事件被onTouchEvent消费,onClick不会触发(注:在父级的onTouchEvent返回true,  不会消费事件,因为已经设置了onClick的监听,说明是该View消费了事件,不会再往上传递)。

猜你喜欢

转载自blog.csdn.net/u011598031/article/details/80548935