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);
}
PhoneWindow是Window的唯一实现类,所以最终会调用到PhoneWindow的superDispatchTouchEvent方法。
接着会调用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消费事件。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/t576163422/article/details/109296226