Android事件分发机制
- 一、被分发的对象
- 二、分发事件的组件
- 三、分发的核心方法
- 四、事件分发过程
- Activity的dispatchTouchEvent方法
- ViewGroup的dispatchTouchEvent方法
- View的dispatchTouchEvent方法
一、被分发的对象
被分发的对象是那些?被分发的对象是用户触摸屏幕而产生的点击事件,事件主要包括:按下、滑动、抬起与取消。这些事件被封装成MotionEvent对象。该对象中的主要事件如下表所示:
事件 | 触发场景 | 单次触发的次数 |
MotionEvent.ACTION_DOWN | 在屏幕按下时 | 1次 |
MotionEvent.ACTION_MOVE | 在屏幕上滑动时 | 0次或多次 |
MotionEvent.ACTION_UP | 在屏幕抬起时 | 0次或1次 |
MotionEvent.ACTION_CANCLE | 滑动超出控件边界时 | 0次或1次 |
按下、滑动、抬起、取消这几种事件组成了一个事件流。事件流以按下为开始,中间可能有多次滑动,以抬起或取消结束。
二、分发事件的组件
分发事件的组件:分发事件者,包括Activity、View和ViewGroup。它们三者的一般结构为:
组件 | 特点 | 例子 |
Activity | 视图类 | 如MainActivity |
ViewGroup | View的容器,可以包含若干View | 各种布局类 |
View | UI类组件的基类 | 如Button、textview |
三、分发的核心方法
负责对事件进行分发的方法主要有三个
dispatchTouchEvent()
onTouchEvent()
onInterceptTouchEvent()
不是所有的组件都有这三个方法:
组件 | dispatchTouchEvent | onTouchEvent | onInterceptTouchEvent |
Activity | 存在 | 存在 | 不存在 |
ViewGroup | 存在 | 存在 | 存在 |
View | 存在 | 存在 | 不存在 |
四、事件分发过程
分发方法dispatchTouchEvent
Activity的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (child.dispatchTouchEvent(ev)) {
return true; //如果子View消费了该事件,则返回TRUE,让调用者知道该事件已被消费
} else {
return onTouchEvent(ev); //如果子View没有消费该事件,则调用自身的onTouchEvent尝试处理。
}
}
当事件传递给Activity后,它先将事件分发给子View处理。
- 如果经过子View层层传递或处理后,该事件被消费了(即返回了true),则Activity的分发方法也返回true,同样也表示该事件已经被消费了。
- 如果经过子View层层传递或处理后,该事件没有被消费(即返回了false),则Activity的分发方法就不会返回true了,而是调用onTouchEvent()去处理,看其实际的处理情况。
- 如果onTouchEvent消费了事件,那依然能返回true(表示已消费事件),这个true作为dispatchTouchEvent的返回值,让调用它的对象知道该Activity已经消费了事件。
- 如果onTouchEvent没有消费该事件,那就返回false(表示未消费事件),这个false作为dispatchTouchEvent的返回值,让调用它的对象知道该Activity没有消费事件,需要继续处理。
ViewGroup的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!onInterceptTouchEvent(ev)) {
return child.dispatchTouchEvent(ev); //不拦截,则传给子View进行分发处理
} else {
return onTouchEvent(ev); //拦截事件,交由自身对象的onTouchEvent方法处理
}
}
ViewGroup的dispatchTouchEvent 多了一个onInterceptTouchEvent方法。当事件传入时,首先会调用onInterceptTouchEvent。
- 如果该方法返回了false(表示不拦截),则交给子View去调用dispatchTouchEvent()方法
- 如果该方法返回了true(表示拦截),则直接交给该ViewGroup对象的onTouchEvent(ev)方法处理,具体是否能处理以onTouchEvent()的实际情况为准。
- 如果 ViewGroup 重写了 onInterceptTouchEvent或者onTouchEvent 并满足拦截条件就会返回true,常见的有自定义手势监听,滑动处理等
View的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
//如果该对象的监听成员变量不为空,则会调用其onTouch方法,
if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
return true; //若onTouch方法返回TRUE,则表示消费了该事件,则dispachtouTouchEvent返回TRUE,让其调用者知道该事件已被消费。
}
return onTouchEvent(ev); //若监听成员为空或onTouch没有消费该事件,则调用对象自身的onTouchEvent方法处理。
}
方法调用后,会先对mOnTouchListener判空,如果之前Set了Listener,则会调用其onTouch方法。
- 若onTouch方法返回TRUE,则dispatchTouchEvent也会返回TRUE,表示消费该事件。
- 若onTouch方法返回FALSE,或者mOnTouchListener本来就是空,则调用自身的onTouchEvent()来处理,是否消费事件,可以由其返回值判断。