View的事件分发机制是Android中的一个难点,也是非常重要的知识点,充分理解和掌握事件分发机制有助于我们在自定义View的时候更好地分析和解决问题
整体上来看,View事件的大致传递顺序是Activity->Window->ViewGroup->View,首先到activity的dispatchTouchEvent,然后到Activity内部的Window,Window再传递给DecorView,由DecorView再到顶级View,顶级View是我们在Activity中用setContentView设置的View,一般是一个ViewGroup,ViewGroup又会把事件分发给它的子view,当然子view可以是ViewGroup也可以是View
dispatchTouchEvent是可以说是所有事件分发的入口函数,其中ViewGroup和View都有dispatchTouchEvent方法,但是它们对事件的处理逻辑是完全不一样的,这里先从ViewGroup的dispatchTouchEvent方法开始分析
ViewGroup的dispatchTouchEvent方法,源码如下:
public boolean dispatchTouchEvent(MotionEvent ev) {
// 省略部分代码..............
// Check for interception.
// mFirstTouchTarget 代表是否有子view消费了事件
final boolean intercepted; //是否拦截事件
//在允许拦截事件的情况下,view的down事件都会调用onInterceptTouchEvent方法,询问是否
//需要拦截事件,还有就是有子view消费事件的时候,也会调用onInterceptTouchEvent方法
//如果没有子view消费事件,代表没必要分发事件了,直接intercepted为true拦截事件
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//是否允许拦截事件,可以自己设置
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) { //允许拦截
//调用了拦截事件的方法,返回Boolean代表是否拦截事件,ViewGroup一般默认不拦截事件,返回false,
//可以重写此方法返回true代表拦截,然后事件将不再往下分发
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// 意思就是当前事件不是down事件并且没有子view消费,所有此viewgroup直接拦截事件
// 也可以这么理解,一开始down事件分发下去,然而下一级view并没有消费事件,那么
// 此后的其他事件如move、up都不再分发给下一级了
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
//如果事件没有取消并且viewgroup不拦截事件,则执行事件的分发
if (!canceled && !intercepted) {
// 省略部分代码...................
//循环遍历ViewGroup的子view,逐一询问子view是否消费事件
final int childrenCount = mChildrenCount; //子view个数
//
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//循环遍历子view,从最后添加的子view开始,代表先分发给后添加的子view
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1