1 事件传递流程概述
事件的传递是由最顶层(即最外层)的父控件开始,一层一层向下传递,直到最底层(即最内层)的子控件。若最底层的子控件没有消耗掉事件,则:事件又会从最底层的子控件开始,一层一层向上传递,直到最顶层的父控件。
当父控件不拦截事件(onInterceptTouchEvent返回true,代表拦截事件)时,事件才会向下传递给其子控件。含有子view的viewGroup默认都不会拦截。当子控件不消耗掉事件(onTouchEvent返回true,代表消耗掉事件)时,事件才会向上传递给其父控件。
以上为完整的事件传递流程,后面我们将知道,只有down事件会走完整的事件传递流程,move、up等后续事件走的都是简化的事件传递流程。
2 事件传递方法
以ViewGroup为例:
public boolean dispatchTouchEvent(MotionEvent ev)
:管理事件的分发,返回值表示当前控件或其直接、间接的子控件是否消耗了事件。这个返回值代表的是事件分发的结果,并不会左右事件分发的走向。public boolean onInterceptTouchEvent(MotionEvent ev)
: 在dispatchTouchEvent方法的内部被调用,返回值表示是否要拦截事件。需要注意的是,一旦拦截了某个事件,那么同一事件序列中的后续事件都不会再经过onInterceptTouchEvent。事件序列:从手指按下,经过移动,到手指抬起,这一系列触摸事件称为一个事件序列。public boolean onTouchEvent(MotionEvent ev)
:处理事件,返回值表示是否消耗当前事件。
以上三个方法的关系可用下面的伪代码来表示:
public boolean dispatchTouchEvent(MotionEvent ev){
boolean comsume = false;
if(onInterceptTouchEvent(ev)){//若拦截,则交给自己的onTouchEvent处理
comsume = onTouchEvent(ev);
}else{//若不拦截,则交给子view
comsume = child.dispatchTouchEvent(ev);
}
return comsume;
}
//注意:View没有onInterceptTouchEvent方法,一旦有触摸事件传递给它,它的onTouchEvent方法就会被调用
可见,onInterceptTouchEvent和onTouchEvent都是在dispatchTouchEvent中被调用的:
- 若onInterceptTouchEvent拦截了事件,则将事件交给自己的onTouchEvent处理
- 若onInterceptTouchEvent未拦截事件,则将事件往下传递给子view的dispatchTouchEvent
在dispatchTouchEvent中可以通过调用requestDisallowInterceptTouchEvent来请求所有的父控件不要拦截事件:
public boolean dispatchTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);// 请求所有父控件不要拦截事件
...
}
效果是所有父控件的onInterceptTouchEvent方法都不会被调用。