
源码分析
下面,咱们一起通过源码,全面解析事件分发机制,即按顺序讲解:
-
Activity事件分发机制 -
ViewGroup事件分发机制 -
View事件分发机制
Activity事件分发机制
????????Android事件分发机制首先会将点击事件传递到Activity中,具体是执行dispatchTouchEvent()进行事件分发。
Activity.dispatchTouchEvent()源码
/**
??*?创建人:帅次
??*?创建时间:2021/7/5
??*?功能:Activity.dispatchTouchEvent()
??*/
public?boolean?dispatchTouchEvent(MotionEvent?ev)?{
????if?(ev.getAction()?==?MotionEvent.ACTION_DOWN)?{
?//在这里仅用于ACTION_DOWN的判断
?onUserInteraction();
????}
????//返回true
????if?(getWindow().superDispatchTouchEvent(ev))?{
??//Activity.dispatchTouchEvent()就返回true,则方法结束。
???//该点击事件停止往下传递&事件传递过程结束(让爷爷吃了嘿嘿)
???return?true;
????}
????return?onTouchEvent(ev);
}
Activity.onUserInteraction()源码
/**
??*?创建人:帅次
??*?创建时间:2021/7/6
??*?功能:该方法是用户交互,每当向Activity分派按键、触摸或轨迹球事件时调用。
??*/
public?void?onUserInteraction()?{
}
Window.superDispatchTouchEvent()源码
/**
??*?创建人:帅次
??*?创建时间:2021/7/6
??*?功能:Window.superDispatchTouchEvent属于抽象方法。
??*?用于自定义窗口,如Dialog,传递触摸屏事件进一步向下视图层次结构。
??*?应用程序开发人员应该不需要实现或调用它。
??*/
public?abstract?boolean?superDispatchTouchEvent(MotionEvent?event);
因为Window是抽象类,咱就继续挖,就找到了它的唯一实现类「PhoneWindow」。
PhoneWindow.superDispatchTouchEvent()源码
//?This?is?the?top-level?view?of?the?window,?containing?the?window?decor.
//这是窗口的顶层View的实例对象。?
private?DecorView?mDecor;
@Override
public?boolean?superDispatchTouchEvent(MotionEvent?event)?{
?//咱们继续往下看
?return?mDecor.superDispatchTouchEvent(event);
}
DecorView.superDispatchTouchEvent()源码
public?boolean?superDispatchTouchEvent(MotionEvent?event)?{
?//super调用父类dispatchTouchEvent方法。那它的父类是谁呢?
?//DecorView?extends?FrameLayout、FrameLayout?extends?ViewGroup!
?//从上面看出DecorView?是ViewGroup的间接子类。
?//看到这里Activity.dispatchTouchEvent()也基本差不多了
?//如果ViewGroup.dispatchTouchEvent?return?true,
?//则Activity.dispatchTouchEvent()return?true。
?//如果ViewGroup.dispatchTouchEvent?return?false,
?//则执行Activity.onTouchEvent(ev)。
?return?super.dispatchTouchEvent(event);
}
Activity.onTouchEvent()源码
/**
??*?创建人:帅次
??*?创建时间:2021/7/6
??*?功能:当Touch事件未被其下的任何View消费时调用。
??*/
public?boolean?onTouchEvent(MotionEvent?event)?{
????//Window.shouldCloseOnTouch来判断是否消费。
????//那咱就继续看看Window.shouldCloseOnTouch是干嘛的
????if?(mWindow.shouldCloseOnTouch(this,?event))?{
????????finish();
????????//已经消费了该事件,则返回true
????????return?true;
????}
????//还没有消费返回false,默认返回false
????return?false;
}
Window.shouldCloseOnTouch()源码
//这里支持的最高版本maxTargetSdk?=?Build.VERSION_CODES.P(28),咱使用SDK30就没办法处理了。
//主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等
@UnsupportedAppUsage(maxTargetSdk?=?Build.VERSION_CODES.P,?trackingBug?=?115609023)
public?boolean?shouldCloseOnTouch(Context?context,?MotionEvent?event)?{
????final?boolean?isOutside?=
????????????event.getAction()?==?MotionEvent.ACTION_UP?&&?isOutOfBounds(context,?event)
????????????||?event.getAction()?==?MotionEvent.ACTION_OUTSIDE;
????if?(mCloseOnTouchOutside?&&?peekDecorView()?!=?null?&&?isOutside)?{
?//return?true:说明事件在边界外,即?消费事件
?return?true;
????}
//返回false:在边界内,即未消费(默认)
????return?false;
}
Activity.onTouchEvent()到这里就基本结束了。后面源码完善在再补充。
Activity源码总结
当一个点击事件发生时,从Activity的事件分发开始(Activity.dispatchTouchEvent()),流程如下:

ViewGroup事件分发机制
????????从上面Activity的事件分发机制可知,在Activity.dispatchTouchEvent()实现了将事件从Activity->ViewGroup的传递,ViewGroup的事件分发机制从dispatchTouchEvent()开始。
????????在Activity.dispatchTouchEvent()中遗留了ViewGroup.dispatchTouchEvent()什么时候返回true/false在下面的源码分析中找出来。
ViewGroup.dispatchTouchEvent()源码
先从宏观角度,纵览整个 dispatch 的源码如下:
@Override
public?boolean?onInterceptTouchEvent(MotionEvent?ev)?{
????/**
?????*?一、检查当前ViewGroup是否需要拦截事件
?????*?1、如果事件为DOWN事件,则调用onInterceptTouchEvent进行拦截判断;
?????*?2、mFirstTouchTarget!=null,代表已经有子View捕获了这个事件,
?????子?View?的?dispatchTouchEvent?返回true就是代表捕获touch?件。
?????*/
????/**
?????*?二、将事件分发给子View
?????*?满足条件canceled和intercepted都为false,既不取消也不拦截
?????*?1、actionMasked==MotionEvent.ACTION_DOWN
?????表明事件主动分发的前提是事件为?DOWN?事件
?????*?2、通过for循环,遍历当前ViewGroup下的所有子View;
?????*?3、处判断事件坐标是否在子?View?坐标范围内,并且子?View?并没有处在动画状态;
?????*?4、调用?dispatchTransformedTouchEvent?方法将事件分发给子?View,
?????如果子?View?捕获事件成功,则将?mFirstTouchTarget?赋值给子?View。
?????*/
????/**
?????*?三、根据mFirstTouchTarget再次分发事件
?????*?3.1、mFirstTouchTarget为null,说明在上述的事件分发中并没有子?View?对事件进行了捕获操作。
?????直接调用?dispatchTransformedTouchEvent?方法,并传入child为?null
?????最终会调用?super.dispatchTouchEvent?方法。实际上最终会调用自身的?onTouchEvent?方法,进行处理touch事件。
?????结论:如果没有子?View?捕获处理?touch?事件,ViewGroup会通过自身的onTouchEvent方法进行处理。
?????*?3.2、mFirstTouchTarget?不为?null,说明在上述的事件分发中有子?View?对?touch?事件进行了捕获,
?????则直接将当前以及后续的事件交给?mFirstTouchTarget?指向的?View?进行处理。
?????*/
}
下面咱逐层分析:
@Override
public?boolean?dispatchTouchEvent(MotionEvent?ev)?{
????//?是否按下操作?,?最终的对外返回结果?,?该方法的最终返回值?
????boolean?handled?=?false;
????/*onFilterTouchEventForSecurity以应用安全策略过滤触摸事件。
????/return?true分派事件,return?false删除事件。*/
????if?(onFilterTouchEventForSecurity(ev))?{
????????final?int?action?=?ev.getAction();
????????final?int?actionMasked?=?action?&?MotionEvent.ACTION_MASK;
????????final?boolean?intercepted;
????????/**
?????????*?一、检查当前ViewGroup是否需要拦截事件
?????????*?1、如果事件为DOWN事件,则调用onInterceptTouchEvent进行拦截判断;
?????????*?2、mFirstTouchTarget!=null,代表已经有子View捕获了这个事件,
?????????子?View?的?dispatchTouchEvent?返回true就是代表捕获touch?件。
?????????*/
????????if?(actionMasked?==?MotionEvent.ACTION_DOWN
????????????????||?mFirstTouchTarget?!=?null)?{
????????????final?boolean?disallowIntercept?=?(mGroupFlags?&?FLAG_DISALLOW_INTERCEPT)?!=?0;
????????????if?(!disallowIntercept)?{
????????????????intercepted?=?onInterceptTouchEvent(ev);
????????????????//恢复操作以防它被更改
????????????????ev.setAction(action);
????????????}?else?{
????????????????intercepted?=?false;
????????????}
????????}?else?{
????????????//?There?are?no?touch?targets?and?this?action?is?not?an?initial?down
????????????//?so?this?view?group?continues?to?intercept?touches.
????????????intercepted?=?true;
????????}
????????//?Check?for?cancelation.
????????final?boolean?canceled?=?resetCancelNextUpFlag(this)
????????????????||?actionMasked?==?MotionEvent.ACTION_CANCEL;
????????//?Update?list?of?touch?targets?for?pointer?down,?if?needed.
????????final?boolean?isMouseEvent?=?ev.getSource()?==?InputDevice.SOURCE_MOUSE;
????????final?boolean?split?=?(mGroupFlags?&?FLAG_SPLIT_MOTION_EVENTS)?!=?0
????????????????&&?!isMouseEvent;
????????TouchTarget?newTouchTarget?=?null;
????????boolean?alreadyDispatchedToNewTouchTarget?=?false;
????????/**
?????????*?二、将事件分发给子View
?????????*?满足条件canceled和intercepted都为false,既不取消也不拦截
?????????*/
????????if?(!canceled?&&?!intercepted)?{
????????????/*1、actionMasked==MotionEvent.ACTION_DOWN表明
????????????事件主动分发的前提是事件为?DOWN?事件*/
????????????if?(actionMasked?==?MotionEvent.ACTION_DOWN
????????????????????||?(split?&&?actionMasked?==?MotionEvent.ACTION_POINTER_DOWN)
????????????????????||?actionMasked?==?MotionEvent.ACTION_HOVER_MOVE)?{
????????????????final?int?actionIndex?=?ev.getActionIndex();?//?always?0?for?down
????????????????final?int?idBitsToAssign?=?split???1?<<?ev.getPointerId(actionIndex)
????????????????????????:?TouchTarget.ALL_POINTER_IDS;
????????????????//?Clean?up?earlier?touch?targets?for?this?pointer?id?in?case?they
????????????????//?have?become?out?of?sync.
????????????????removePointersFromTouchTargets(idBitsToAssign);
????????????????final?int?childrenCount?=?mChildrenCount;
????????????????if?(newTouchTarget?==?null?&&?childrenCount?!=?0)?{
????????????????????final?float?x?=
????????????????????????????isMouseEvent???ev.getXCursorPosition()?:?ev.getX(actionIndex);
????????????????????final?float?y?=
????????????????????????????isMouseEvent???ev.getYCursorPosition()?:?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;
????????????????????//2、通过for循环,遍历当前ViewGroup下的所有子View;
????????????????????for?(int?i?=?childrenCount?-?1;?i?>=?0;?i--)?{
????????????????????????final?int?childIndex?=?getAndVerifyPreorderedIndex(
????????????????????????????????childrenCount,?i,?customOrder);
????????????????????????final?View?child?=?getAndVerifyPreorderedView(
????????????????????????????????preorderedList,?children,?childIndex);
????????????????????????//3、处判断事件坐标是否在子?View?坐标范围内,并且子?View?并没有处在动画状态;
????????????????????????if?(!child.canReceivePointerEvents()
????????????????????????????????||?!isTransformedTouchPointInView(x,?y,?child,?null))?{
????????????????????????????continue;
????????????????????????}
????????????????????????newTouchTarget?=?getTouchTarget(child);
????????????????????????if?(newTouchTarget?!=?null)?{
????????????????????????????//?Child?is?already?receiving?touch?within?its?bounds.
????????????????????????????//?Give?it?the?new?pointer?in?addition?to?the?ones?it?is?handling.
????????????????????????????newTouchTarget.pointerIdBits?|=?idBitsToAssign;
????????????????????????????break;
????????????????????????}
????????????????????????resetCancelNextUpFlag(child);
????????????????????????/*4、调用?dispatchTransformedTouchEvent?方法将事件分发给子?View,
?????????????????????????如果子?View?捕获事件成功,则将?mFirstTouchTarget?赋值给子?View。*/
????????????????????????if?(dispatchTransformedTouchEvent(ev,?false,?child,?idBitsToAssign))?{
????????????????????????????//?Child?wants?to?receive?touch?within?its?bounds.
????????????????????????????mLastTouchDownTime?=?ev.getDownTime();
????????????????????????????if?(preorderedList?!=?null)?{
????????????????????????????????//?childIndex?points?into?presorted?list,?find?original?index
????????????????????????????????for?(int?j?=?0;?j?<?childrenCount;?j++)?{
????????????????????????????????????if?(children[childIndex]?==?mChildren[j])?{
????????????????????????????????????????mLastTouchDownIndex?=?j;
????????????????????????????????????????break;
????????????????????????????????????}
????????????????????????????????}
????????????????????????????}?else?{
????????????????????????????????mLastTouchDownIndex?=?childIndex;
????????????????????????????}
????????????????????????????mLastTouchDownX?=?ev.getX();
????????????????????????????mLastTouchDownY?=?ev.getY();
????????????????????????????newTouchTarget?=?addTouchTarget(child,?idBitsToAssign);
????????????????????????????alreadyDispatchedToNewTouchTarget?=?true;
????????????????????????????break;
????????????????????????}
????????????????????}
????????????????????if?(preorderedList?!=?null)?preorderedList.clear();
????????????????}
????????????}
????????}
????????/**
?????????*?三、根据mFirstTouchTarget再次分发事件
?????????*/
????????//3.1mFirstTouchTarget为null,
????????if?(mFirstTouchTarget?==?null)?{
????????????/*说明在上述的事件分发中并没有子?View?对事件进行了捕获操作。
????????????直接调用?dispatchTransformedTouchEvent?方法,并传入child为?null
????????????最终会调用?super.dispatchTouchEvent?方法。
????????????实际上最终会调用自身的?onTouchEvent?方法,进行处理touch事件。*/
????????????/*结论:如果没有子?View?捕获处理?touch?事件,
????????????ViewGroup会通过自身的onTouchEvent方法进行处理。*/
????????????handled?=?dispatchTransformedTouchEvent(ev,?canceled,?null,
????????????????????TouchTarget.ALL_POINTER_IDS);
????????}?else?{
????????????//?Dispatch?to?touch?targets,?excluding?the?new?touch?target?if?we?already
????????????//?dispatched?to?it.??Cancel?touch?targets?if?necessary.
????????????TouchTarget?predecessor?=?null;
????????????TouchTarget?target?=?mFirstTouchTarget;
????????????while?(target?!=?null)?{
????????????????final?TouchTarget?next?=?target.next;
????????????????if?(alreadyDispatchedToNewTouchTarget?&&?target?==?newTouchTarget)?{
????????????????????handled?=?true;
????????????????}?else?{
????????????????????final?boolean?cancelChild?=?resetCancelNextUpFlag(target.child)
????????????????????????????||?intercepted;
????????????????????/*mFirstTouchTarget?不为?null,说明在上述的事件分发中有子?View?对?touch?事件进行了捕获,
????????????????????则直接将当前以及后续的事件交给?mFirstTouchTarget?指向的?View?进行处理。
?????????????????????*/
????????????????????if?(dispatchTransformedTouchEvent(ev,?cancelChild,
????????????????????????????target.child,?target.pointerIdBits))?{
????????????????????????handled?=?true;
????????????????????}
????????????????}
????????????????predecessor?=?target;
????????????????target?=?next;
????????????}
????????}
????}
????return?handled;
}
ViewGroup.onInterceptTouchEvent()源码
/**
??*?创建人:帅次
??*?创建时间:2021/7/6
??*?前提ViewGroup.dispatchTouchEvent?return?super.dispatchTouchEvent(ev);
??*?功能:是否拦截事件?return?true拦截,return?false(默认)不拦截
??*/
public?boolean?onInterceptTouchEvent(MotionEvent?ev)?{
????if?(ev.isFromSource(InputDevice.SOURCE_MOUSE)
????????????&&?ev.getAction()?==?MotionEvent.ACTION_DOWN
????????????&&?ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
????????????&&?isOnScrollbarThumb(ev.getX(),?ev.getY()))?{
????????return?true;
????}
????return?false;
}
ViewGroup.onTouchEvent()源码
????????ViewGroup是没有onTouchEvent()这个方法的,因为ViewGroup是View的子类,所以ViewGroup可以直接调用View的onTouchEvent()方法。这里就不做详细介绍,下面在View事件分发机制中一起解答。
ViweGroup源码总结
????????Android事件分发传递到Acitivity后,总是先传递到ViewGroup、再传递到View。流程总结如下:(假设已经经过了Acitivity事件分发传递并传递到ViewGroup)

View的事件分发机制
从上面ViewGroup事件分发机制知道,View事件分发机制从dispatchTouchEvent()开始。
View.dispatchTouchEvent()源码
public?boolean?dispatchTouchEvent(MotionEvent?event)?{
????/*一、(mViewFlags?&?ENABLED_MASK)?==?ENABLED
?????1、该条件是判断当前点击的控件是否enable
?????2、由于很多View默认enable,故该条件恒定为true(除非手动设置为false)
?????*/
????/*二、mOnTouchListener?!=?null
?????1、mOnTouchListener变量在View.setOnTouchListener()里赋值
?????2、即只要给控件注册了Touch事件,mOnTouchListener就一定被赋值(即不为空)
?????*/
?????/*三、mOnTouchListener.onTouch(this,?event)
?????1、即回调控件注册Touch事件时的onTouch();
?????2、需手动复写设置,具体如下(以View为例)
?????view.setOnTouchListener(new?OnTouchListener()?{
????????@Override
????????public?boolean?onTouch(View?v,?MotionEvent?event)?{
????????????MLog.logEvent("MyTouchTrueView.onTouch自行处理:",event);
????????????return?true;
????????????//?1、若在onTouch()返回true,就会让上述三个条件全部成立,
????????????从而使得View.dispatchTouchEvent()直接返回true,事件分发结束
????????????//?2、若在onTouch()返回false,就会使得上述三个条件不全部成立,
????????????从而使得View.dispatchTouchEvent()中跳出if,执行onTouchEvent(event)
????????????//?3、onTouchEvent()源码分析?
????????}
????});
?????*/
????if?(?(mViewFlags?&?ENABLED_MASK)?==?ENABLED?&&
????????????mOnTouchListener?!=?null?&&
????????????mOnTouchListener.onTouch(this,?event))?{
????????return?true;
????}
????return?onTouchEvent(event);
}
View.onTouchEvent()源码
public?boolean?onTouchEvent(MotionEvent?event)?{??
????...?//?仅展示关键代码
????//?若该控件可点击,则进入switch判断中
????if?(((viewFlags?&?CLICKABLE)?==?CLICKABLE?||?(viewFlags?&?LONG_CLICKABLE)?==?LONG_CLICKABLE))?{??
????????//?根据当前事件类型进行判断处理
????????switch?(event.getAction())?{?
????????????//?抬起View
????????????case?MotionEvent.ACTION_UP:??
????????????????????performClick();?
????????????????????break;??
????????????//?按下
????????????case?MotionEvent.ACTION_DOWN:??
????????????????postDelayed(mPendingCheckForTap,?ViewConfiguration.getTapTimeout());??
????????????????break;??
????????????//?取消
????????????case?MotionEvent.ACTION_CANCEL:??
????????????????refreshDrawableState();??
????????????????removeTapCallback();??
????????????????break;
????????????//?滑动
????????????case?MotionEvent.ACTION_MOVE:??
????????????????final?int?x?=?(int)?event.getX();??
????????????????final?int?y?=?(int)?event.getY();??
????????????????int?slop?=?mTouchSlop;??
????????????????if?((x?<?0?-?slop)?||?(x?>=?getWidth()?+?slop)?||??
????????????????????????(y?<?0?-?slop)?||?(y?>=?getHeight()?+?slop))?{??
????????????????????removeTapCallback();??
????????????????????if?((mPrivateFlags?&?PRESSED)?!=?0)?{??
????????????????????????removeLongPressCallback();??
????????????????????????mPrivateFlags?&=?~PRESSED;??
????????????????????????refreshDrawableState();??
????????????????????}??
????????????????}??
????????????????break;??
????????}??
????????//?若该控件可点击,就一定返回true
????????return?true;??
????}??
??//?若该控件不可点击,就一定返回false
??return?false;??
}
??public?boolean?performClick()?{??
??????if?(mOnClickListener?!=?null)?{
??????????//?只要通过setOnClickListener()为控件View注册1个点击事件
??????????//?那么就会给mOnClickListener变量赋值(即不为空)
??????????//?则会往下回调onClick()?&?performClick()返回true
??????????playSoundEffect(SoundEffectConstants.CLICK);??
??????????mOnClickListener.onClick(this);??
??????????return?true;??
??????}??
??????return?false;??
??}??
View源码总结
????????Android事件分发传递到Acitivity后,总是先传递到ViewGroup、再传递到View。流程总结如下:(假设已经经过了ViewGroup事件分发传递并传递到View)

总结
重点分析了 dispatchTouchEvent 的事件的流程机制,这一过程主要分 3 部分:
-
1、判断是否需要拦截 —> 主要是根据 onInterceptTouchEvent 方法的返回值来决定是否拦截; -
2、在 DOWN 事件中将 touch 事件分发给子 View —> 这一过程如果有子 View 捕获消费了 touch 事件,会对 mFirstTouchTarget 进行赋值; -
3、最后一步,DOWN、MOVE、UP 事件都会根据 mFirstTouchTarget 是否为 null,决定是自己处理 touch 事件,还是再次分发给子 View。
然后介绍了整个事件分发中的几个特殊的点。
-
1、DOWN 事件的特殊之处:事件的起点;决定后续事件由谁来消费处理; -
2、mFirstTouchTarget 的作用:记录捕获消费 touch 事件的 View,是一个链表结构; -
3、CANCEL 事件的触发场景:当父视图先不拦截,然后在 MOVE 事件中重新拦截,此时子 View 会接收到一个 CANCEL 事件。
以上就是本文的全部内容,希望对大家学习 Android 事件分发有所帮助和启发。
|