Android开发艺术探索笔记——View(二)
View的事件分发机制
学习资料:
1.Understanding Android Input Touch Events System Framework
2.Managing Touch Events in a ViewGroup
3.Android事件传递机制
4.Input Events
5.Mastering the Android Touch System
6.MotionEvent
MotionEvent(运动事件)的传递规则
用户每次触摸屏幕都被包装成了MotionEvent(运动事件)对象。
属性有:
-
动作码(action code),如
ACTION_DOWN
,ACTION_UP
等等,用于描述用户当前的动作。 - 触摸的横纵坐标。
- 其它信息,如压力,大小以及方向等等。
View的事件分发,就是对MotionEvent事件的分发过程。
事件分发的三个重要方法:
//用于分发事件(dispatch touch event),要么将事件向下传递到目标View,要么交由自己处理。
//返回true表示自己处理
public boolean dispatchTouchEvent (MotionEvent event)
//用于拦截事件(intercept touch event),ViewGroup中有,View中没有这个方法。
public boolean onInterceptTouchEvent (MotionEvent event)
//用于处理事件
public boolean onTouchEvent (MotionEvent event)
三个方法的关系可用如下伪代码描述:
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
if(onInterceptTouchEvent(ev)){
consume = true;
}else{
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
View的onTouchListener
的优先级比onTouchEvent
方法的高。
运动事件的传递顺序:
Activity-->Window-->View
下面是将View
的dispatchTouchEvent()
方法设置断点后,点击ImageView
的调试过程:
能清楚地看到事件的传递过程和顺序。
若View的onTouchEvent()
方法返回false,则会调用它的父View的onTouchEvent()
方法,依此类推,若调用顺序上的所有View都不处理这个事件,则这个事件会最终传递给Activity的onTouchEvent()
方法。
View的事件分发机制类似于互联网公司的工作流程:
新任务:
CEO-->产品经理-->CTO-->开发小组组长-->程序员
由上至下一级一级分发任务(dispatchTouchEvent),如果是自己的任务(onInterceptTouchEvent)
,则拦截自己处理(onTouchEvent),反之,则交由下级分发(child.dispatchTouchEvent)。
如果事情搞不定,就一级一级向上抛(parent.onTouchEvent):
程序员-->开发组长-->CTO-->产品经理-->CEO
事件传递机制的一些结论:
- 1.事件序列:从手指接触屏幕到手指离开屏幕的过程,ACTION_DOWN-->ACTION_MOVE-->...-->ACTION_MOVE-->ACTION_UP
- 2.一个事件序列只能被一个View拦截且消费。
- 3.ViewGroup默认不拦截事件。源码中ViewGroup的
onInterceptTouchEvent()
方法默认返回false - 4.View没有
onInterceptTouchEvent()
方法 - 5.事件传递是由外向内(由上至下)的。事件先传递给父元素,然后再由父元素分发给子元素。通过
requestDisallowInterceptTouchEvent()
方法可以在子元素中干预父元素的事件分发过程。
事件分发源码解析
Activity对事件的分发过程
- 1.点击事件首先传递给
Activity
,然后由Activity
的dispatchTouchEvent()
方法进行事件的分发。Activity会将事件交由window进行分发。
//Activity源码
...
/*
* Activity的dispatchTouchEvent方法
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//Activity交由window进行事件分发
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
- 2.Window将事件传递给DecorView(即ContentView的父View,可通过
Activity.getWindow().getDecorView()
方法获取)。而Window类是抽象类,superDispatchTouchEvent()
方法是抽象方法。
//Window类是抽象类
public abstract class Window {
...
//window的superDispatchTouchEvent方法是抽象方法
public abstract boolean superDispatchTouchEvent(MotionEvent event);
...
}
而PhoneWindow类是Window类的唯一实现类。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
...
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//PhoneWindow直接将事件交友DecorView处理
return mDecor.superDispatchTouchEvent(event);
}
...
}
可以看到PhoneWindow类在实现抽象方法superDispatchTouchEvent
时,直接将事件交由DecorView处理。