android事件分发机制
Android事件分发
Android中的事件其实就是一个触摸事件或者案件事件,是通过MotionEvent这个对象进行传递的,所谓对事件的分发其实就是对MotionEvent的分发,当一个事件产生后,系统会把这个事件分发到一个具体的View对象。Activity是一个层级的结构,它的事件分发起点是Activity,终点是末端的view,如果view不处理该事件,事件又会以一个反向的过程传递。
上图是Android的窗口结构,其实window是通过一个叫ViewRootImpl的对象来管理整个view树的。
在Activity中事件分发是从函数dispatchTouchEvent()开始的
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
上面代码我们重点关注getWindow().superDispatchTouchEvent(ev),Activity将事件分发到自己的window对象中去处理,因为Activity中Window的具体实现是PhoneWindow,所以我们接着去看PhoneWindow中是怎么处理的
@Override
public boolean superDispatchKeyEvent(KeyEvent event) {
return mDecor.superDispatchKeyEvent(event);
}
window 将事件分发给了mDecor对象去处理,上面的图中可以看出,DecorView可以看做是整个View树根,事件从这里开始向下传递。
在view中事件传递这个过程主要借助三个函数完成的
dispatchTouchEvent(), onTouchEvent()和onInterceptTouchEvent(),下面介绍一下这三个函数的大体功能。
- dispatchTouchEvent(MotionEvent e)
如果事件被分发到了当前的View(ViewGroup也是View),那这个函数必定会被调用,它的返回结果受自身的onTouchEvent()和下级View影响。如果返回false表示没有消耗该事件,否则消耗了该事件。 - onTouchEvent(MotionEvent e)
在这个方法中处理事件,如果返回了false,则一个事件序列中的其他事件不会再分发到这里。 - onInterceptTouchEvent(MotionEvent e)
这个方法在View中是没有的,只在ViewGroup中有,表示是否拦截该事件,如果拦截则不会再向下传递。如果拦截了该事件,同一个事件中的事件不会再调用到这里。
那这三个方法有什么关系呢?这里借用Android开发艺术这本书上的一段伪代码来描述一下
public boolean dispatchTouchEvent(MotionEvent e){
boolean consume = false;
if(onInterceptTouchEvent(e)){
consume = onTouchEvent(e);
}else{
consume = child.dispatchTouchEvent(e);
}
return consume;
}
从上面的伪代码我们知道事件处理的大体流程是,如果当前的View决定拦截该事件,那么会调用自身的onTouchEvent()进行处理,事件不向下传递;如果当前view不拦截该事件,那么会调用子view的dispatchTouchEvent()方法将事件分发给自己的子view去处理,如果子view不处理,也就是返回false,那这个事件又会向上传递。
下面我们总结一下事件分发机制的一些特点
- 同一个事件序列指的是,从手指触摸屏幕到抬起的国产中产生的事件,包括一个Down事件,0-多个move事件,一个UP事件。
- 一个事件序列不能同时交给多个View同时处理,一旦一个ViewGroup拦截了某个事件,那么这个事件序列中的其他事件都会交给他来处理,并且该事件序列中的其他事件不会再调用onInterceptTouchEvent来决定是否拦截。
- 如果onTouchEvent中处理Down事件时返回了false,就表示它不处理该事件,那么同一个事件序列中的其他事件就不会再交给它来处理,会将事件重新交给它的父元素处理。
- ViewGroup默认不拦截任何事件
- view默认会消耗事件,即onTouchEvent返回true, 除非这个view是不可以点击的,即clickable和longClickable都是false.
- 事件传递是由内到外的,即Activity->Window->View,如果最底层的view不处理,那么事件会返回给上一层处理。
- child中requestDisallowInterceptTouchEvent() 可以在子元素中干预父元素的事件分发过程,但是无法干预 ACTION_DOWN 事件。
- 可以给view设置监听器检测触摸事件,它们的调用优先级顺序是这样的setOnTouchListener() => onTouchEvent() => onClickListener()
更多推荐
所有评论(0)