Android事件分发代码流程分析

Android事件分发流程

事件流程图

触摸事件流程图

事件类型

MotionEvent.ACTION_DOWN

手指初次接触到屏幕时触发。

MotionEvent.ACTION_MOVE

手指在屏幕上滑动时触发,触发执行多次。

MotionEvent.ACTION_UP

手指离开屏幕时触发。

MotionEvent.ACTION_CANCEL

事件被上层拦截时触发。

Activity#dispatchTouchEvent分发事件

/**
 * Activity#dispatchTouchEvent分发事件
 */
public boolean dispatchTouchEvent(MotionEvent ev) {	
	//调用PhoneWindow分发事件
	if (getWindow().superDispatchTouchEvent(ev)) {
		return true;
	}
	//返回false 表示所有View都不处理该事件,交给Activtiy的onTouchEvent处理
	return onTouchEvent(ev);
}

在Activity中进行分发事件,调用Window的抽象方法superDispatchTouchEvent继续进行事件分发。如果返回false,表示没有任何View处理事件,由Activity进行事件处理。

Window的实现类PhoneWindow分发事件

/**
 * PhoneWindow#superDispatchTouchEvent
 */
public boolean superDispatchTouchEvent(MotionEvent event) {
	//通过decorView调用superDispatchTouchEvent方法。
	return mDecor.superDispatchTouchEvent(event);
}
/**
 * DecorView#superDispatchTouchEvent
 * DecorView继承自FrameLayout。调用super.dispatchTouchEvent()方法
 */
public boolean superDispatchTouchEvent(MotionEvent event) {
	//通过decorView最终会调用dispatchTouchEvent方法。
	return super.dispatchTouchEvent(event);
}
  1. PhoneWindow是Window的唯一实现类,所以最终会调用到PhoneWindow的superDispatchTouchEvent方法。
  2. 接着会调用DecorView的superDispatchTouchEvent方法。而DecorView继承自FrameLayout,最后会调用到ViewGroup#dispatchTouchEvent方法。

ViewGroup#diapatchTouchEvent分发事件

DOWN1.x.x是ACTION_DOWN事件处理流程;
MOVE2.x.x是ACTION_MOVE事件处理流程;

dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
    }
    if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
        ev.setTargetAccessibilityFocus(false);
    }

    boolean handled = false;
    if (onFilterTouchEventForSecurity(ev)) {//过滤事件
        final int action = ev.getAction();
        final int actionMasked = action & MotionEvent.ACTION_MASK;

        //DOWN1.1:ACTION_DOWN事件时重置状态,->DOWN1.2
        //MOVE2.1:MOVE事件未命中if条件,->MOVE2.2
        if (actionMasked == MotionEvent.ACTION_DOWN) {
        	//ACTION_DOWN命中if条件重置状态
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }

        final boolean intercepted;
        //DOWN1.2:ACTION_DOWN事件命中if条件,mFirstTouchTarget默认为null,->DOWN1.3
        //MOVE2.2:mFirstTouchTarget在DOWN事件时已经赋值,不为null,命中if条件,->MOVE2.3
        if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            /*
             * DOWN1.3
             * 在down事件中disallowIntercept肯定为false,因为在上面resetTouchState方法中会重
             * 置mGroupFlags属性,导致判断为false。命中if条件
             * (可以通过requestDisallowInterceptTouchEvent方法修改mGroupFlags变量,
             * 从而修改disallowIntercept改变是否拦截的条件)
             */
            /*
             * MOVE2.3
             * 如果在down事件中赋值disallowIntercept为false,后续又没有修改为ture,
             * 那么move事件中还是保持原样,默认为false,命中if条件
             */
            if (!disallowIntercept) {
                //不重写onInterceptTouchEvent方法的前提下,通常会返回false
                //ACTION_DOWN事件->DOWN1.4
                //ACTION_MOVE事件->MOVE2.4
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action);
            } else {
                intercepted = false;
            }
        } else {
			intercepted = true;
        }
        if (intercepted || mFirstTouchTarget != null) {
            ev.setTargetAccessibilityFocus(false);
        }
        final boolean canceled = resetCancelNextUpFlag(this)
                || actionMasked == MotionEvent.ACTION_CANCEL;

        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
        TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        /*
         * DOWN1.4:判断intercepted是否拦截事件
         * false:不拦截事件,命中if条件,进行事件分发,->DOWN1.5.1
         * true:拦截事件,未命中if条件,进行事件处理,->DOWN1.6.1
         *
         * MOVE2.4:判断intercepted是否拦截事件
         * false:不拦截事件,命中if条件,->MOVE2.5.1
         * true:拦截事件,未命中if条件,->MOVE2.5.2
         */
        if (!canceled && !intercepted) {
            View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                    ? findChildWithAccessibilityFocus() : null;
            /*
             * DOWN1.5.1:判断当前事件为ACTION_DOWN,命中if条件,进行事件分发,->DOWN1.5.2
             * MOVE2.5.1:判断当前事件为ACTION_MOVE,未命中if条件,不事件分发->MOVE2.5.2
             */
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                final int actionIndex = ev.getActionIndex();
                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                        : TouchTarget.ALL_POINTER_IDS;

                removePointersFromTouchTargets(idBitsToAssign);

                final int childrenCount = mChildrenCount;
                /*
                 * DOWN1.5.2
                 * 此时newTouchTarget为null
                 * 判断childrenCount != 0是否有子View,
                 * 没有子View未命中if条件->DOWN1.6.1,
                 * 有子View命中if条件->DOWN1.5.3。
                 */
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);
                    /*
                     * DOWN1.5.3
                     * 调用buildTouchDispatchChildList方法,获取childView集合并排序
                     * (通过xml的view层级Z轴排序)
                     * ->DOWN1.5.4
                     */
                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
                    //DOWN1.5.4:循环遍历子view ->DOWN1.5.5
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        //校验childIndex是否越界
                        final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
                        //校验childView是否为null
                        final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
                        if (childWithAccessibilityFocus != null) {
                            if (childWithAccessibilityFocus != child) {
                                continue;
                            }
                            childWithAccessibilityFocus = null;
                            i = childrenCount - 1;
                        }
                        /*
                         * DOWN1.5.5
                         * 判断此子View是否可以响应事件?
                         * 是否显示?
                         * 触摸事件是否在子View的范围内?
                         * 如果未命中条件则跳过此View
                         * 命中条件则->DOWN1.5.6
                         */
                        if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            continue;
                        }
                        /**
                         * DOWN1.5.6
                         * 调用getTouchTarget方法->DOWN1.5.7进入方法,
                         * 查看方法后发现返回值为null,因此newTouchTarget仍然为null
                         * ->DOWN1.5.8
                         */
                        newTouchTarget = getTouchTarget(child);
                        if (newTouchTarget != null) {
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            break;
                        }
                        resetCancelNextUpFlag(child);
                        /*
                         * DOWN1.5.8
                         * 调用dispatchTransformedTouchEvent进行分发事件,
                         * child参数不为null->DOWN1.5.9
                         * 然后根据返回值判断是否处理
                         * true:child处理此事件,命中if条件,break循环->DOWN1.5.10。
                         * false:child不处理该事件,继续遍历。如果没有child处理->DOWN1.6.1,
                         */
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            mLastTouchDownTime = ev.getDownTime();
                            if (preorderedList != null) {
                                for (int j = 0; j < childrenCount; j++) {
                                    if (children[childIndex] == mChildren[j]) {
                                        mLastTouchDownIndex = j;
                                        break;
                                    }
                                }
                            } else {
                                mLastTouchDownIndex = childIndex;
                            }
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            /*
                             * DOWN1.5.10
                             * 调用addTouchTarget方法,给newTouchTarget赋值,查看该方法->DOWN1.5.11,
                             * 然后赋值给newTouchTarget,
                             * 其实就是把mFirstTouchTarget赋给newTouchTarget,
                             * 所以mFirstTouchTarget == newTouchTarget为true
                             * alreadyDispatchedToNewTouchTarget为true,后续用到
                             * ->DOWN1.5.12
                             */
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            //跳出循环
                            break;
                        }
                        ev.setTargetAccessibilityFocus(false);
                    }
                    if (preorderedList != null) preorderedList.clear();
                }
                //newTouchTarget不为null,未命中if条件
                if (newTouchTarget == null && mFirstTouchTarget != null) {
                    newTouchTarget = mFirstTouchTarget;
                    while (newTouchTarget.next != null) {
                        newTouchTarget = newTouchTarget.next;
                    }
                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                }
            }
        }
        /**
         * DOWN1.6.1
         * 拦截事件或没有child处理事件,此时mFirstTouchTarget为null。
         * 命中if条件->DOWN1.6.2
         */      
        //MOVE2.5.2:mFirstTouchTarget在DOWN事件时已经赋值,不为null,未命中if条件,->MOVE2.5.3
        if (mFirstTouchTarget == null) {
            /*
             * DOWN1.6.2
             * 调用dispatchTransformedTouchEvent方法,此时child参数为null->DOWN1.6.3
             */
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
            //ACTION_DOWN事件完成,由自己来决定是否处理事件,调用super.dispatchTouchEvent并返回
        } else {
            TouchTarget predecessor = null;
            /*
             * DOWN1.5.12
             * mFirstTouchTarget不为null(已经在DOWN1.5.11处赋值),进入else
             * 赋值给target,进入循环,while循环只会执行一次
             * why?
             * 因为在循环体中target.next会再赋值给target,
             * 而通过DOWN1.5.11处得知mFirstTouchTarget.next为null,
             * 所以只会循环一次(单点触控case)
             * ->DOWN1.5.13
             *
             * MOVE2.5.3
             * mFirstTouchTarget不为null,赋值给target,进入循环
             * while循环会执行一次
             * 在循环体中target.next会再赋值给target,
             * 在ACTION_DOWN事件DOWN1.5.11得知mFirstTouchTarget.next为null,
             * 所以会循环一次(单点触控case)
             * ->MOVE2.5.4
             */
            TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                final TouchTarget next = target.next;
                /**
                 * DOWN1.5.13
                 * 通过DOWN1.5.10处得知
                 * alreadyDispatchedToNewTouchTarget为true,
                 * mFirstTouchTarget == newTouchTarget也为true,
                 * 所以target == newTouchTarget也为true,命中if条件。
                 * handled为true,完成down事件,dispatchTouchEvent返回true,由child处理事件
                 */
                 /**
                  * MOVE2.5.4
                  * alreadyDispatchedToNewTouchTarge被重置,默认为false
                  * newTouchTarget被重置,默认为null
                  * 未命中if条件,->MOVE2.5.5
                  */
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    //ACTION_DOWN事件完成
                    handled = true;
                } else {
                    //判断是否拦截事件
                    final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
                    /*
                     * MOVE2.5.5
                     * 调用dispatchTransformedTouchEvent方法,传递cancelChild参数
                     * ->MOVE2.6.1
                     */
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                       	//确定本次事件的返回值     
                        handled = true;
                    }
                    if (cancelChild) {
						/**
						 * MOVE2.6.5:
						 * 事件已经被本层拦截,predecessor默认为null,命中if
						 * next也为null(MOVE2.5.3处已经说明),mFirstTouchTarget也会重置为null
						 * 后续再有事件流到本层,就执行类似DOWN1.6.1的流程,
						 * 因为mFirstTouchTarget为null,而且只有在down事件并找到可以执行事件的子View时
						 * 才会被赋值
						 * move事件完成
						 */
                        if (predecessor == null) {
                            mFirstTouchTarget = next;
                        } else {
                            predecessor.next = next;
                        }
                        target.recycle();
                        target = next;
                        continue;
                    }
                }
                predecessor = target;
                target = next;
            }
        }

        if (canceled
                || actionMasked == MotionEvent.ACTION_UP
                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
            resetTouchState();
        } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
            final int actionIndex = ev.getActionIndex();
            final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
            removePointersFromTouchTargets(idBitsToRemove);
        }
    }

    if (!handled && mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
    }
    return handled;
}
getTouchTarget方法
/**
 * DOWN1.5.7
 * 在down事件中,mFirstTouchTarget到现在一直还未被赋值,还是为null,无法进入循环体
 * 所以返回null
 */
private TouchTarget getTouchTarget(@NonNull View child) {
    for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
        if (target.child == child) {
            return target;
        }
    }
    return null;
}
addTouchTarget方法
private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
    //DOWN1.5.11:调用TouchTarget.obtain方法,创建TouchTarget对象
    final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
    //此时mFirstTouchTarget还是为null,所以target.next也为null
    target.next = mFirstTouchTarget;
    //把target赋值给mFirstTouchTarget,(mFirstTouchTarget终于不为null了o(╥﹏╥)o)
    mFirstTouchTarget = target;
    return target;
}
dispatchTransformedTouchEvent方法
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) {
    final boolean handled;
    final int oldAction = event.getAction();
    /**
     * MOVE2.6.1
     * cancel为true:拦截事件,命中if条件->MOVE2.6.2
     * cancel为false:不拦截事件,未命中if-> MOVE2.5.6
     */  
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
        //MOVE2.6.2:准备拦截事件,更改事件为ACTION_CANCEL事件->MOVE2.6.3
        event.setAction(MotionEvent.ACTION_CANCEL);
        /*
         * MOVE2.6.3
         * child不为null,未命中if条件
         * child在ACTION_DOWN事件-DOWN1.5.11处赋值,这个child其实就是要处理或分发事件的子View,
         * 因为事件被拦截,所以child会收到cancel事件
         * ->MOVE2.6.4
         */
        if (child == null) {
            //接收到的是ACTION_CANCEL事件
            handled = super.dispatchTouchEvent(event);
        } else {
            //child接收到的ACTION_CANCEL事件
            handled = child.dispatchTouchEvent(event);
        }
        //MOVE2.6.4:还原成原来的事件,然后返回 ->MOVE2.6.5
        event.setAction(oldAction);
        return handled;
    }

    final int oldPointerIdBits = event.getPointerIdBits();
    final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
    if (newPointerIdBits == 0) {
        return false;
    }
    final MotionEvent transformedEvent;
    if (newPointerIdBits == oldPointerIdBits) {
        if (child == null || child.hasIdentityMatrix()) {
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                final float offsetX = mScrollX - child.mLeft;
                final float offsetY = mScrollY - child.mTop;
                event.offsetLocation(offsetX, offsetY);

                handled = child.dispatchTouchEvent(event);

                event.offsetLocation(-offsetX, -offsetY);
            }
            return handled;
        }
        transformedEvent = MotionEvent.obtain(event);
    } else {
        transformedEvent = event.split(newPointerIdBits);
    }

    if (child == null) {
        //DOWN1.6.3:child参数为null,调用View.dispatchTouchEvent方法,自己决定是否处理事件
        handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        final float offsetX = mScrollX - child.mLeft;
        final float offsetY = mScrollY - child.mTop;
        transformedEvent.offsetLocation(offsetX, offsetY);
        if (! child.hasIdentityMatrix()) {
            transformedEvent.transform(child.getInverseMatrix());
        }
        /*
         * DOWN1.5.9:调用child的dispatchTouchEvent方法,进行分发或处理事件。
         * 如果child是View就执行View#dispatchTouchEvent(android.view.MotionEvent)处理事件
         * 如果child是ViewGroup就执行ViewGroup#dispatchTouchEvent(android.view.MotionEvent)分发事件
         */
        /*
         * MOVE2.5.6:传递MOVE事件给child
         * 如果child是View就执行View#dispatchTouchEvent(android.view.MotionEvent)处理事件
         * 如果child是ViewGroup就执行ViewGroup#dispatchTouchEvent(android.view.MotionEvent)分发事件
         */
        handled = child.dispatchTouchEvent(transformedEvent);
    }
    transformedEvent.recycle();
    //可以看到此时handled是child或者父类View的返回值
    return handled;
}
总结

ACTION_DOWN的事件中,会重置mGroupFlags变量,所以默认调用onInterceptTouchEvent方法,判断本层View是否拦截事件,并赋值给intercepted字段。
通过intercepted判断是否拦截事件。
不拦截事件:
就遍历本层childView,找到继续传递事件的View,调用dispatchTransformedTouchEvent方法,在该方法内会调用child.dispatchTouchEvent方法,然后通过child.dispatchTouchEvent方法返回值来确定该事件是否被处理。返回为true表示处理事件,会调用addTouchTarget方法给mFirstTouchTarget变量赋值,记录由哪个child进行处理事件。
并且在确定由哪个View处理事件的同时会把alreadyDispatchedToNewTouchTarget置为true。后续根据mFirstTouchTarget 和 alreadyDispatchedToNewTouchTarget字段确认事件已经被哪个childView处理,然后返回为true处理事件。如果没有子View处理事件,则和拦截事件的流程是一样的。
拦截事件:
跳过遍历childView阶段,通过mFirstTouchTarget == null为判断,表示没有childView处理或者事件被拦截,调用super.dispatchTouchEvent方法,分发给自己处理。

ACTION_MOVE的事件中,会根据mFirstTouchTarget判断是否有处理事件的childView,如果没有直接由本层处理。如果有也会mGroupFlags变量计算出是否要调用onInterceptTouchEvent方法拦截。不过无论拦截与否,Move事件都不会再遍历寻找childView,mFirstTouchTarget只有可能在Down事件中被赋值。
所以,Move事件里要么mFirstTouchTarget为null,由自己来处理事件。要么就是得到mFirstTouchTarget获取传递事件的childView,事件继续向下分发。如果本层View打算拦截事件,会把当前的MOVE事件修改成Cancel事件向下传递,这样childView接收到的就是Cancel事件。然后把mFirstTouchTarget置为null,这样后续的事件不会再继续向下传递了。
我们可以得出如下结论
1:Down事件中会确定把事件交给给谁来处理,如果都不处理就由Activity来处理。
2:对于叶节点View,Down事件没有处理,那么MOVE事件也处理不了。
3:Down事件中会重置mGroupFlags,所以肯定会调用onInterceptTouchEvent方法。
4:子View返回false为什么会调用本层的onTouchEvent?因为子View返回false不处理,本层会执行和拦截一样的流程(mFirstTouchTarget为null),会分配给本层进行处理。

事件处理流程

View#dispatchTouchEvent分发事件

/**
 * View#dispatchTouchEvent
 * 调用View的dispatchTouchEvent方法分发处理事件
 */
public boolean dispatchTouchEvent(MotionEvent event) {
	//如果该View是enable,并且设置了OnTouchListener,
	//根据onTouch方法的返回值判断是否执行onTouchEvent方法
	if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
		mOnTouchListener.onTouch(this, event)) {
        return true;
    }
    //在onTouchEvent中进行一系列判断,然后消费事件
	return onTouchEvent(event);

View#dispatchTouchEvent进行分发事件,在view是enable的前提下,如果已设置setOnTouchListener,并且onTouch方法返回true,那么View自己的onTouchEvent就不会被执行了。
如果没有设置setOnTouchListener或者onTouch方法返回false,继续执行onTouchEvent方法。

View#onTouchEvent消费事件

/**
 * View#onTouchEvent
 * 调用View的onTouchEvent方法消费事件
 */
public boolean onTouchEvent(MotionEvent event) {
	final int action = event.getAction();
	if (clickable) {
		switch (action) {
			case MotionEvent.ACTION_UP:
				if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
					if (!focusTaken) {
						if (mPerformClick == null) {
							mPerformClick = new PerformClick();
						}
						if (!post(mPerformClick)) {
							performClick();
						}
					}
				}
			 break;
		}
		return true;
	}
	return false;
}

/**
 * View#performClick
 * 处理click事件
 */
public boolean performClick() {
	final boolean result;
	final ListenerInfo li = mListenerInfo;
	if (li != null && li.mOnClickListener != null) {
		playSoundEffect(SoundEffectConstants.CLICK);
		li.mOnClickListener.onClick(this);
		result = true;
	} else {
		result = false;
	}
	return result;
}

在onTouchEvent中进行一系列判断,判断当前控件是否为clickable等等。
进入switch中进行事件判断,如果当前的事件是ACTION_UP。再判断是否有焦点等等。然后会执行performClick方法。如果mOnClickListener不为空,就调用onClick方法。最后返回true消费事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值