关于Android的事件分发机制,内容分为一下两点:
1.Android的事件分发的基础知识
2.Android事件的传递规则
基础知识
1.1 什么是事件
Android中的事件是指用户在手指在接触到屏幕所产生的一系列活动,典型的事件类型有如下几种:
- ACTION_DOWN:手指与屏幕接触
- ACTION_MOVE:手指在屏幕上移动
- ACTION_UP:手指抬起
- ACTION_CANCEL:因某种原因取消
用户的触摸操作会产生一系列的点击事件,我们称之为事件序列,典型的事件序列的组成:ACTION_DOWN → n* ACTION_MOVE → ACTION_UP。即一个事件序列是由一个按下事件加上若干个移动事件再加上一个抬起事件组成,任何事件列都是以DOWN事件开始,UP事件结束,中间有无数的MOVE事件。
1.2参与事件分发的对象
参与事件分发的对象有三类:Activity(Window)、ViewGroup、View。当一个事件产生时,事件的传递顺序是:Activity(Window) -> ViewGroup -> View。
Activity(Window) 是指UI最上层的容器。
View是所有UI组件的基类,一般的控件都是View的派生类。
ViewGroup是容纳UI组件的容器,即一组View的集合(包含很多子View和子VewGroup)。ViewGroup本身也是从View派生的,即ViewGroup是View的子类。
1.3事件分发
事件分发,是要解决用户所产生的事件序列是事件由哪个对象发出,经过哪些对象,最终达到哪个对象并最终得到处理。
2.事件的传递规则
当一个MotionEvent产生了之后,系统就需要把这个事件传递给某一个具体的View,这个传递的过程就是分发的过程。事件的传递由三个特重要的方法来完成:dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent() 。
- dispatchTouchEvent() :事件分发,当某个事件能传递到该View时,这个方法就一定会被调用。返回结果受到onInterceptTouchEvent()与onTouchEvent()方法的影响,表示是否消费当前事件。
- onInterceptTouchEvent(): 事件拦截,在dispatchTouchEvent()方法内部调用。如果当前View拦截了某个事件,那么在同一事件序列中,该方法不会再被调用。返回的结果表示是否拦截事件。
- onTouchEvent() :该方法用来处理事件,返回结果为是否消耗当前事件。如果不消耗,则当前的事件序列无法再被当前View接收。
上述三个方法可以由一下伪代码总结:
public boolean dispatchTouchEvent(MotionEvent ev){
boolean a=false;
if(onInterceptTouchEvent(ev)){
a=onTouchEvent(ev);
}else{
a=child. dispatchTouchEvent(ev);
}
return a;
}
通过上面的伪代码。我们也能了解到时间分发的规则:对于一个根ViewGroup来说,当一个事件产生后,首先会传递给它,这时它的dispatchTouchEvent()方法会被调用,如果这个View的onInterceptTouchEvent(ev)方法返回true时,就表示要拦截事件,事件会交由该ViewGroup来处理。接着该View的onTouchEvent()就会被调用;如果这个View的onInterceptTouchEvent(ev)方法返回false时,表示不拦截该事件,这是事件就会传递给子元素,子元素的dispatchTouchEvent()方法会被调用,如此反复,直到事件被处理掉。
那么问题来了,如果事件传递到View树的最底层事件还是没有被消费掉,这时系统会怎么处理?
点击事件的分发总会依照如下顺序:Activity → Window → View。
当所有控件都不去处理时,这时事件会回传给Activity去处理,即调用Activity的onTouchEvent()方法。
值得注意的一点:当View设置了onTouchListener时,那么它的onTouch()方法会被回调,这时的事件处理要看onTouch()的返回值。如果返回false发,那么该View的onTouchEvent()会被调用,反之则不会。
由此看来onTouchListener的优先级比onTouchEvent要高。
下面是来自《Android开发艺术探索》的总结:
1.同一个事件序列是指从手指接触屏幕开始到离开屏幕这系列产生的一系列事件。事件序列时候由DOWN事件开始,中间若干个MOVE事件,最终以UP事件结束。
2. 正常情况下,一个事件序列只能由一个View拦截并消耗。
3. 当View决定拦截事件,那么该事件序列只能由该View来处理,并且它的onInterceptTouchEvent()不会再被调用。
4. 当某一View开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么接下来的事件都不会再交给它处理。并且将事件交由它的父元素去处理。
5. 如果Activity不消耗除ACTION_DOWN以外的事件,那么该点击事件会消失,这时它的父元素也不会去处理。最终,该事件会交由Activity去处理。
6.ViewGroup默认不拦截任何事件,即 ViewGroup 的 onInterceptTouchEvent默认返回false。
7. VIew没有 onInterceptTouchEvent方法,一旦有事件传递,onTouchEvent会被调用。
8. View的onTouchEvent默认都会消耗事件,除非它是不可点击的(clickable与longClickable同时为false)。
9. View的enable属性不影响onTouchEvent的默认返回值。
10. onclick的前提时当前View是可点击的,并且收到的down与up事件。
11. 事件的传递是由外向内的,总是从父控件开始,然后传递给子View。但通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程。但Action_Down事件除外。
以上,就是Android事件分发机制的一些总结。
内容总结于《Android开发艺术探索》与网络博客,如有侵权,请联系删除。