自定义View(一):事件分发
一.touch事件分发
1.View的事件分发逻辑其实很简单,从手指触摸手机屏幕开始,系统将该事件绑定到MotionEvent对象上,从Activity的根布局DecorView开始向下进行传递。
简化后的代码逻辑为:
//当前ViewGroup处理点击事件
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
//是否拦截该事件
if (onIntercepterTouchEvent(ev)) {
//拦截则调用onTouchEvent进行处理
consume = onTouchEvent(ev);
} else {
//不拦截则当前View的子View处理该点击事件,子View\"递归判断\"
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
2.ViewGroup处理touch事件:
是否拦截?
拦截:则自己处理逻辑,事件分发就此终止。
不拦截:则传递给子布局,ViewGroup是默认不拦截touch事件。如果子View还是一个ViewGroup,那么继续重复上述流程,直到最后一个布局为View。由于View没有子布局,所以View也不存在onIntercepterTouchEvent拦截函数
二.Activity的onTouchEvent(),View的onTouch(),View的onTouchEvent()之间是什么关系?
1.Activity的onTouchEvent()
根据代码的追踪,Activity的onTouchEvent(),最终是在根布局DecorView中进行处理,
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
当mFeatureId < 0的时候,执行activity的onTouchEvent()函数,否则,执行DecorView的dispatchTouchEvent()函数,也就是上面写的“事件分发”流程。 mFeatureId < 0是什么意思?源码注释中解释到:
/** The feature ID of the panel, or -1 if this is the application's DecorView */
即,当mFeatureId < 0的时候,表示该panel容器是Actvity/Dialog本身,只有DecorView布局,没有额外的子布局。
也就是说:
当touch事件是分发给只含有DecorView布局的Activity本身,则直接执行Activity的onTouchEvent方法。
否则:
通过DecorView向下层分发事件。
简单的说,子View处理事件的优先级高于Activity
2.View的onTouch()和View的onTouchEvent()
简化一下View的dispatchTouchEvent()函数源码:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
可以看出,mOnTouchListener.onTouch()的优先级高于onTouchEvent(),调用了onTouch(),就不再调用onTouchEvent(),且它们处理的是同一个event事件。
3.三者之间的关系:
1.优先级View的onTouch()>View的onTouchEvent()>Activity的onTouchEvent()
2.View的onTouch()和View的onTouchEvent()函数处理都是同一个event事件,onTouch()处理了,则onTouchEvent()不再处理。
3.onTouchEvent()也有返回值,假如最终的子View的onTouchEvent()返回为false,则改事件会反馈到它的父级viewGroup的onTouchEvent()进行处理,如果一直返回false,则一直反馈到Activity的onTouchEvent()。