序言

在 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继承关系

ANDROID 如何让父布局放弃获取焦点 android父布局拦截点击事件_应用程序

事件处理机制的几个重要方法

dispatchTouchEvent(): 用于分发触摸事件到视图层次结构中的各个视图。该方法通常由父视图调用,以便将触摸事件传递给子视图或进行其他处理。

onInterceptTouchEvent(): 用于拦截触摸事件。如果一个视图的onInterceptTouchEvent()方法返回true,表示该视图希望拦截触摸事件并自己处理,而不将事件传递给其子视图。

onTouchEvent(): 用于处理触摸事件的逻辑。在这个方法中,可以根据事件类型(如ACTION_DOWN、ACTION_MOVE等)执行相应的操作,例如处理滑动手势、点击事件等。

事件冲突解决方式

内部拦截法

在内部拦截法中,子视图通过重写 onInterceptTouchEvent() 方法来决定是否拦截触摸事件。

子视图可以根据自己的逻辑和条件判断是否需要拦截触摸事件。

如果子视图拦截了触摸事件,那么后续的触摸事件(如 MOVE、UP 等)将由子视图自己处理。

如果子视图不拦截触摸事件,那么触摸事件将继续传递给父容器处理。

此时,子视图可以通过调用 getParent().requestDisallowInterceptTouchEvent(true)方法来请求父容器不要拦截后续触摸事件。

外部拦截法

在外部拦截法中,父容器监听子视图的触摸事件,并根据一定的逻辑决定是否拦截事件。

当父容器拦截触摸事件时,子视图将无法直接处理这些事件,而由父容器处理。

当父容器不拦截触摸事件时,子视图正常处理这些事件。