Android View的事件分发机制
阅读《Android开发艺术探索》提炼实践总结
所谓事件分发?
所谓点击事件分发,其实就是对MotionEvent事件的分发过程,即当一个MotionEvent产生了以后,系统需要把这个事件传递给一个具体的View,而这个传递的过程就是分发过程。
点击事件的分发过程有三个很重要的方法来共同完成!
dispatchTouchEvent(MotionEvent ev)
用于事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。
onInterceptTouchEvent(MotionEvent event)
View没有这个方法,Activity也没有,只有ViewGroup才有
在dispatchTouchEvent方法中调用,用来判断是否拦截摸一个事件,如果当前View拦截了某个事件,那么在同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
onTouchEvent(MotionEvent event)
在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。
这三个方法的关系时什么呢?
我超级喜欢下面的伪代码,将关系道的淋漓尽致
public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume = false; if (onInterceptTouchEvent(ev)) { consume = onTouchEvent(ev); } else { consume = child.dispatchTouchEvent(ev); } return consume; }
一些需要注意的小tips:
- 传递机制:Activity->Window->View,即事件总时先传递给Activity,Activity在传递给Window,最后Window在传递给顶级View,顶级View接收到事件后,就会按照事件分发机制去分发事件。
- 如果所有的元素都不处理这个事件,那么这个事件最后将会传递给Activity,即Activity的onTouchEvent方法会被调用。
- ViewGroup默认不拦截任何事件。
- View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)。View的longClickable属性默认都为false,clickable属性要分情况,比如Button的clickable属性默认为true,TextView的clickable属性默认为false。
- View的enable(作用时设置其是否可用)属性不影响onTouchEvent的默认返回值。
- 事件传递过程时由外向内的,即事件总是传递给父元素,然后在由父元素分发给子View,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。