Android事件分发机制,是Android开发中的重点及难点,掌握事件分发机制,可以更好地解决自定义控件、view之间的滑动冲突等问题。网上并不缺少这类博客文章,但是没有比自己总结理解,记忆更为深刻吧!
分发机制由以下三个重要方法组成
public boolean dispatchTouchEvent(MotionEvent event)
- 用于事件分发,如果事件能够传入当前viewGroup,那么将由此方法往子view进行事件传递,返回的结果由内部的onInterceptTouchEvent和onTouchEvent方法决定
public boolean onInterceptTouchEvent(MotionEvent event)
- 在dispatchTouchEvent方法中调用,返回结果用于判断是否拦截某个事件,值得注意的是,只有ViewGroup拥有该方法,而View没有,毕竟View不包含子View,无法拦截。
public boolean onTouchEvent(MotionEvent event)
- 在dispatchTouchEvent方法中调用,假如viewGroup拦截某个事件,则会触发此方法,大多用于处理触摸事件,返回结果表示是否消耗对应事件,若不消耗事件,则无法继续接收同类事件。
针对ViewGroup事件分发的伪代码总结
public boolean dispatchTouchEvent(MotionEvent ev) {
//是否消费事件
boolean consume = false;
//是否拦截事件
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
//将事件分发到子view中去
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
概括:当触摸事件发生时,ViewGroup首先触发dispatchTouchEvent方法,然后根据onInterceptTouchEvent的返回结果判断是否拦截该事件,如拦截该方法,那么这个ViewGroup就会调用onTouchEvent(前提是设置了onTouchListener);如果不拦截,那么viewGroup就会继续向其子View继续传递事件,即子View调用dispatchTouchEvent方法,然后继续上述过程,直到事件被处理。
流程图
源码分析
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
通过上述代码可以知道,首先是activity将事件交给附属的window进行分发,如果window的分发结果返回true,那么触摸事件将分发到ViewGroup,否则就将事件交给activity处理,即onTouchEvent
通过源码,我们再来分析window是如何将事件分发到viewGroup
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
}
window是一个抽象类,上面的代码实现的是PhoneWindow,然后我们在PhoneWindow类里找到以下的方法
...
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
...
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
PhoneWindow又将事件交给了DecorView,它是PhoneWindow的内部类,并且是一个继承FrameLayout的ViewGroup,并且根据DecorView生成根部局的ViewGroup,所以这里的DecorView其实在某种意义上是整个ViewTree的最顶层View,代表了整个应用的界面。兜兜转转,activity最终也是通过ViewGroup的dispatchTouchEvent方法来分发事件。