序言
在 Android 中,事件分发机制是指在用户与应用程序交互时,事件(如点击、触摸、滑动等)是如何被传递和处理的一套机制。
事件接收和处理的步骤
1 当用户在设备上进行交互
时(如点击、触摸屏幕),操作系统会将相应的输入事件
发送给应用程序。
2 应用程序的窗口管理器
(Window Manager)负责将输入事件传递给正确的窗口。
3 在 Android 中,每个窗口都有一个对应的 ViewRootImpl
对象,它是窗口的根视图(Root View)的处理者。
4 当一个窗口被创建并显示在屏幕上时,ViewRootImpl 对象会创建一个名为 WindowInputEventReceiver
的接收器,用于接收输入事件。
5 当一个输入事件到达时,操作系统将其发送到 WindowInputEventReceiver
,由它负责处理。
6 在 WindowInputEventReceiver 中,输入事件被放入一个队列
中等待处理。
7 ViewRootImpl 对象会定期检查输入事件队列
,并按照顺序处理其中的事件。
8 当一个事件从队列中取出时,它会首先经过一系列的 InputStage
阶段。
9 不同的 InputStage 实现类对应着不同的事件处理
阶段,例如 ViewPostImeInputStage、ViewPreImeInputStage 等。
10 在每个 InputStage 中,事件可以被处理、转换或传递
到下一个阶段
。
11 当事件到达 ViewPostImeInputStage
阶段时,会调用 DecorView.dispatchTouchEvent()
方法,将事件发送给最顶层的视图
。
12 DecorView 是整个应用程序窗口的根视图
,通常是一个 FrameLayout。
13 在 DecorView.dispatchTouchEvent()
方法中,事件会基于触摸位置和其他属性被分发给子元素(即子 View)
处理。
14 子 View 可以决定是否消耗事件(处理事件)并做出响应
。
15 事件最终到达交互目标
的子 View,该子 View 的 onTouchEvent()
方法会被调用,实现针对事件的具体处理逻辑。
触摸事件类型
ACTION_DOWN:
当手指初次接触到屏幕时触发。通常在用户按下触摸屏幕时触发该事件。可以用于实现点击、拖动等功能。
ACTION_MOVE:
当手指在屏幕上滑动时触发,可能会多次触发。当用户按住屏幕并移动手指时,会不断触发该事件,可以用于实现滑动、拖动等交互效果。
ACTION_UP:
当手指离开屏幕时触发。通常在用户松开触摸屏幕时触发该事件。可以用于实现点击、按钮释放等功能。
ACTION_CANCEL:
当事件被上层拦截时触发。如果其他控件或者触摸监听器拦截了当前事件,那么事件传递将被取消,触发ACTION_CANCEL事件。通常用于通知触摸事件的取消,清除相应的状态或进行一些特殊处理。
View继承关系
事件处理机制的几个重要方法
dispatchTouchEvent():
用于分发触摸事件到视图层次结构中的各个视图。该方法通常由父视图调用,以便将触摸事件传递给子视图或进行其他处理。
onInterceptTouchEvent():
用于拦截触摸事件。如果一个视图的onInterceptTouchEvent()方法返回true,表示该视图希望拦截触摸事件并自己处理,而不将事件传递给其子视图。
onTouchEvent():
用于处理触摸事件的逻辑。在这个方法中,可以根据事件类型(如ACTION_DOWN、ACTION_MOVE等)执行相应的操作,例如处理滑动手势、点击事件等。
事件冲突解决方式
内部拦截法
在内部拦截法中,子视图通过重写 onInterceptTouchEvent()
方法来决定是否拦截触摸事件。
子视图可以根据自己的逻辑和条件判断是否需要拦截触摸事件。
如果子视图拦截了触摸事件,那么后续的触摸事件(如 MOVE、UP 等)将由子视图自己处理。
如果子视图不拦截触摸事件,那么触摸事件将继续传递给父容器处理。
此时,子视图可以通过调用 getParent().requestDisallowInterceptTouchEvent(true)
方法来请求父容器不要拦截后续触摸事件。
外部拦截法
在外部拦截法中,父容器监听子视图的触摸事件,并根据一定的逻辑决定是否拦截事件。
当父容器拦截触摸事件时,子视图将无法直接处理这些事件,而由父容器处理。
当父容器不拦截触摸事件时,子视图正常处理这些事件。