一、View的层级关系
二、View的事件分发机制
1、MotionEvent ——点击事件
点击事件用MotionEvent来表示
-
ACTION_DOWN:手指刚接触屏幕
-
ACTION_MOVE:手指在屏幕上移动
-
ACTION_UP:手指从屏幕上松开的一瞬间
点击事件的事件分发,就是对MotionEvent事件的分发过程,即当一个MotionEvent产生了以后,系统需要把这个事件传递给一个具体的View,这个传递过程就是分发过程。
2、事件分发机制——点击事件的传递规则
一次完整的事件分发过程会包括三个阶段,即事件的发布、拦截、消费。这三个阶段分别对应声明在View和ViewGroup中的三个方法,即dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。注意,View不包含onInterceptTouchEvent方法。
(1)Touch事件如何从屏幕到达APP
用户触摸屏幕/按键操作
硬件驱动收到事件,将触摸事件包装为Event存到/dev/input/event[x]目录下
SystemServer进程中的InputManagerService负责与硬件通信,接受屏幕输入事件,内部的读线程InputReader会从系统的/dev/input/目录拿到任务,并分发给InputDispatcher线程
App进程的主线程监听socket客户端,收到输入事件
NativeInputEventReceiver.handleEvent
InputEventReceiver.dispatchInputEvent【即-->】
ViewRootImpl.WindowInputEventReceiver.dispachInputEvent
ViewRootImpl.WindowInputEventReceiver.onInputEvent
ViewRootImpl.enqueueInputEvent
(2)Touch事件到达APP后怎么传递到对应页面
ViewRootImpl.enqueueInputEvent(InputEvent event, InputEventReveiver receiver, int flags, boolean processImmediately)
ViewRootImpl.doProcessInputEvent()
ViewRootImpl.deliverInputEvent(QueuedInputEvent q) //注1 见下一篇文章
InputStage.deliver(QueuedInputEvent q) //注2 见下一篇文章
ViewPostImeInputStage.onProcess
ViewPostImeInputStage.processPointerEvent
DecorView.dispatchPointerEvent
DecorView.dispatchTouchEvent
(3)事件在Activity、Window、DecorView中的传递
在Activity、PhoneWindow、DecorView中,最先接收到触摸事件的是DecorView
DecorView.dispatchTouchEvent
Activity.dispatchTouchEvent
PhoneWindow.superDispatchTouchEvent
DecorView.superDispatchTouchEvent
ViewGroup.dispatchTouchEvent //DecorView调用其父类的方法,本质上还是在DecorView内部调用
1)事件没被拦截、事件被消费
ViewGroup没拦截ACTION_DOWN事件且有View消费该事件:
//假设当前已经递归到了 DecorView
//ACTION_DOWN事件 ACTION_DOWN事件
DecorView.dispatchTouchEvent //即ViewGroup.dispatchTouchEvent
DecorView.onInterceptTouchEvent //没拦截
DecorView.dispatchTransformedTouchEvent
... ...
MyViewgroup.dispatchTouchEvent
MyViewgroup.onInterceptTouchEvent //没拦截
MyViewgroup.dispatchTransformedTouchEvent
MyView.dispatchTouchEvent
MyView.onTouchEvent //return true
MyView.dispatchTouchEvent //return true
MyViewgroup.dispatchTransformedTouchEvent //true
newTouchTarget=mFirstTouchTarget=MyView
MyViewgroup.dispatchTouchEvent //return true
... ...
DecorView.dispatchTransformedTouchEvent //true
newTouchTarget=mFirstTouchTarget=child //child为DecorView中被touch的那个子ViewGroup
DecorView.dispatchTouchEvent
ACTION_MOVE、ACTION_UP的事件分发过程同上
2)没被拦截、没被消费
如果底层View不消费,则默认一步步往上执行父元素onTouchEvent方法。
如果所有View的onTouchEvent方法都返回false,则最后会执行到Activity的onTouchEvent方法,事件分发也就结束了。
ACTION_DOWN事件:
DecorView.dispatchTouchEvent
Activity.dispatchTouchEvent
PhoneWindow.superDispatchTouchEvent
DecorView.superDispatchTouchEvent
ViewGroup.dispatchTouchEvent //DecorView
ViewGroup.onInterceptTouchEvent //没拦截
ViewGroup.dispatchTransformedTouchEvent(child)
...
MyViewgroup.dispatchTouchEvent
MyViewgroup.onInterceptTouchEvent //没拦截
MyViewgroup.dispatchTransformedTouchEvent(child)
MyView.dispatchTouchEvent
MyView.onTouchEvent //return false
MyView.dispatchTouchEvent //return false
MyViewgroup.dispatchTransformedTouchEvent(child) //false
MyViewgroup.dispatchTransformedTouchEvent(null)
View.dispatchTouchEvent
View.OnTouchEvent //return false 调用OnTouchEvent
MyViewgroup.dispatchTouchEvent //return false
...
ViewGroup.dispatchTransformedTouchEvent(child) //return false
ViewGroup.dispatchTransformedTouchEvent(null)
View.dispatchTouchEvent
View.OnTouchEvent //return false 调用OnTouchEvent
ViewGroup.dispatchTouchEvent //return false
DecorView.superDispatchTouchEvent //return false
PhoneWindow.superDispatchTouchEvent //return false
Activity.dispatchTouchEvent
Activity.onTouchEvent //return false 调用OnTouchEvent
DecorView.dispatchTouchEvent
ACTION_MOVE、ACTION_UP事件:
DecorView.dispatchTouchEvent
Activity.dispatchTouchEvent
PhoneWindow.superDispatchTouchEvent
DecorView.superDispatchTouchEvent
ViewGroup.dispatchTouchEvent //DecorView 注意不再执行拦截判断(ACTION_DOWN 或 mFirstTouchTarget!=null 时才走)
ViewGroup.dispatchTransformedTouchEvent(child:null)
View.dispatchTouchEvent
View.OnTouchEvent //return false 在DecorView中调用一次OnTouchEvent
View.dispatchTouchEvent //return false
ViewGroup.dispatchTransformedTouchEvent(child:null) //return false
ViewGroup.dispatchTouchEvent //return false
DecorView.superDispatchTouchEvent //return false
PhoneWindow.superDispatchTouchEvent //return false
Activity.dispatchTouchEvent
Activity.onTouchEvent //在Activity中调用一次OnTouchEvent
DecorView.dispatchTouchEvent
3)被拦截
① 被拦截、被消费
如果 ViewGroup 自身拦截且消费了 ACTION_DOWN 事件,即 onInterceptTouchEvent 和 onTouchEvent 两个方法均返回了 true,那么本次事件序列的后续事件就都会交由其进行处理(如果能接收得到的话),在设置拦截的那个ViewGroup中不会再调用其 onInterceptTouchEvent 方法来判断是否进行拦截
ACTION_DOWN事件:
DecorView.dispatchTouchEvent
Activity.dispatchTouchEvent
PhoneWindow.superDispatchTouchEvent
DecorView.superDispatchTouchEvent
ViewGroup.dispatchTouchEvent
ViewGroup.onInterceptTouchEvent
ViewGroup.dispatchTransformedTouchEvent(child)
MyViewGroup.dispatchTouchEvent
MyViewGroup.onInterceptTouchEvent //true 被拦截
MyViewGroup.dispatchTransformedTouchEvent(null)
View.dispatchTouchEvent
View.OnTouchEvent //return true
View.dispatchTouchEvent //return true
MyViewGroup.dispatchTransformedTouchEvent(null) //return true
MyViewGroup.dispatchTouchEvent //return true
ViewGroup.dispatchTransformedTouchEvent(child) //return true
newTouchTarget=mFirstTouchTarget=MyViewGroup //给mFirstTouchTarget赋值
ViewGroup.dispatchTouchEvent //return true
DecorView.superDispatchTouchEvent //return true
PhoneWindow.superDispatchTouchEvent //return true
Activity.dispatchTouchEvent //return true
DecorView.dispatchTouchEvent
ACTION_MOVE、ACTION_UP事件:
DecorView.superDispatchTouchEvent
ViewGroup.dispatchTouchEvent
ViewGroup.onInterceptTouchEvent
ViewGroup.dispatchTransformedTouchEvent(child)
MyViewGroup.dispatchTouchEvent //MyViewGroup在ACTION_MOVE、ACTION_UP事件中就不再拦截了 因为执行到这里时mFirstTouchTarget=null
MyViewGroup.dispatchTransformedTouchEvent(null)
View.dispatchTouchEvent
View.OnTouchEvent //return true
View.dispatchTouchEvent //return true
MyViewGroup.dispatchTransformedTouchEvent(null) //return true
MyViewGroup.dispatchTouchEvent //return true
ViewGroup.dispatchTransformedTouchEvent(child) //return true
ViewGroup.dispatchTouchEvent //return true
DecorView.superDispatchTouchEvent //return true
② 被拦截、没有被消费
而如果 ViewGroup 拦截了 ACTION_DOWN 事件,但是 onTouchEvent 方法中又没有消费掉该事件的话,那么本次事件序列的后续事件都不会再被其接收到,而是直接交由父视图进行处理。
参考文章:
一文读懂 View 事件分发机制
解决这 8 个问题,Android事件分发再往前一步
看一遍就忘不了,事件分发机制分析——dispatchTouchEvent