iew的事件分发机制说白了就是点击事件的传递,也就是一个Down事件,若干个Move事件,一个Up事件构成的事件序列的传递。
当你手指按了屏幕,点击事件就会遵循Activity->Window→View这一顺序传递。
这一传递过程有三个重要的方法,分别是:
- boolean dispatchTouchEcent(MotionEvent ev),
- boolean onInterceptTouchEvent(MotionEvent event),
- boolean onTouchEvent(MotionEvent event)
先一个一个简单介绍下:
dispatchTouchEcent:
只要事件传递到了当前View,那么dispatchTouchEcent方法就一定会被调用。返回结果表示是否消耗当前事件。
onInterceptTouchEvent:
在dispatchTouchEcent方法内部调用此方法,用来判断是否拦截某个事件。如果当前View拦截了某个事件,那么在这同一个事件序列中,此方法不会再次被调用。返回结果表示是否拦截当前事件。
onTouchEvent:
在dispatchTouchEcent方法内调用此方法,用来处理事件。返回结果表示是否处理当前事件,如果不处理,那么在同一个事件序列里面,当前View无法再收到后续的事件。
当点击事件传递到根ViewGroup里,会执行dispatchTouchEvent,在其内部会先调用onInterceptTouchEvent询问是否拦截事件,若拦截,则执行onTouchEvent方法处理这个事件;
若不拦截,则执行子元素的dispatchTouchEvent,进入向下分发的传递,直到事件被处理。
在处理一个事件的时候,是有优先级的,如果设置了OnTouchListener,会先执行其内部的onTouch方法,这时若onTouch方法返回true,那么表示事件被处理了,不会向下传递了;
如果返回了false,那么事件会继续传递给onTouchEvent方法处理,在onTouchEvent方法中如果当前设置了OnClickListener,那么就会调用其onClick方法。
所以其优先级为:OnTouchListen>onTouchEvent>OnClickListen。
这里有一种情况,如果一个View的onTouchEvent返回了false,那么它父容器的onTouchEvent方法将会被调用。
既然如此,在开头我们说过事件的传递顺序是Activity->Window→View,所以如果所有的元素都返回了false,那么最后事件就会再次传递到Activity里,由Activity的onTouchEvent方法来处理。
这三种方法的对比:
简单举例:
单击button时,会输出log如下:
可以看到,不管是DOWN,MOVE,UP都会按照下面的顺序执行:
1、dispatchTouchEvent
2、 setOnTouchListener的onTouch
3、onTouchEvent
View的dispatchTouchEvent的源码: