1. 触摸事件的定义
Android系统处理用户交互的核心机制之一,它决定了触摸事件如何从系统传递到应用中具体组件并最终被处理消费。它的实现是基于责任链模式,由于触摸事件是包含ACTION_DOWN
、ACTION_MOVE
以及ACTION_UP
的一组事件,当ACTION_DOWN
事件交由某个组件处理后,这组事件剩余事件后续都由它处理。
2. 系统层事件传递过程
- 触摸事件发生后,**IMS(Input Manager Service)**接收事件,**IRS(Input Reader)**读取事件,**IDS(Input Dispatcher)**分发事件。
- 通过管道跨进程将事件信息传递到对应应用操作的
Activity
的ViewRootImpl
对象中。 ViewRootImpl
创建时初始化了**IER(InputEventReceiver)**对象,IER
持有管道,因此该跨进程得以实施。ViewRootImpl
拿到触摸事件后,将其封装成一个Message
对象,传递到**AT(ActivityThread)**的MsgQ
(Message Queue)中。- 如果触摸事件在5秒内未被处理,则系统会弹出**ANR(Application Not Responding)**提示。
- 正常交由
Activity
的dispatchTouchEvent
方法执行,Activity
的分发方法是应用处理触摸事件的入口。
3. 应用层事件处理消费
包含三个方法:分发 、拦截 、消费。
3.1 Activity
的分发方法
- 首先执行
Activity
的dispatchTouchEvent
方法:- 如果返回
true
,则交由Activity
消费。 - 如果返回
false
,则交由Activity
的onTouchEvent
方法处理(无论返回值是什么都不处理)。
- 如果返回
- 默认返回时会走到
Activity
的顶层布局DecorView
中。
3.2 DecorView
和ViewGroup
的处理规则
DecorView
本身是一个LinearLayout
(即ViewGroup
),无论是DecorView
还是其子ViewGroup
,都遵循以下规则:- 先执行本身的
dispatchTouchEvent
方法:- 如果返回
true
,代表处理消费事件。 - 如果返回
false
,交由父组件的onTouchEvent
方法执行。
- 如果返回
- 默认情况执行本身的
onInterceptTouchEvent
方法:- 如果返回
true
,则交由自己的onTouchEvent
方法处理。 - 如果返回
false
,则继续往下分发。
- 如果返回
- 默认情况下,如果有子View被命中 ,则继续分发;否则相当于返回
true
,由自己处理。
- 先执行本身的
- 对于
ViewGroup
的onTouchEvent
方法:- 返回
true
,表示消费处理。 - 返回
false
,则交由父组件的onTouchEvent
方法处理。
- 返回
3.3 View
的处理规则
View
组件只有dispatchTouchEvent
和onTouchEvent
方法。dispatchTouchEvent
方法返回true
,则代表事件由自己处理消费。- 返回
false
,则交由父组件的onTouchEvent
方法处理。 - 默认情况下,根据
View
是否可点击:- 不可点击 :交由父组件的
onTouchEvent
方法处理。 - 可点击 :自己执行
onTouchEvent
方法。
- 不可点击 :交由父组件的
- 消费方法执行优先级 :
OnTouchListener
>onTouchEvent
>OnClickListener
。
4. 事件分发解决冲突
4.1 同向滑动冲突(如ScrollView
包裹ListView
)
- 使用官方标准的
NestedScrollView
,内部通过同步状态来处理事件的消费。 - 使用内部模式 和外部模式 处理事件的消费:
- 外部模式 :处理父组件的
onInterceptTouchEvent
方法,根据条件在ACTION_MOVE
事件中返回false
。 - 内部模式 :子组件通过
requestDisallowInterceptTouchEvent
方法申请父组件不拦截。
- 外部模式 :处理父组件的
- 禁用子组件的事件消费:适用于应用数据量较小且能完全展示的情况。
4.2 不同方向滑动冲突
- 根据滑动方向处理事件消费。
4.3 混合情况
- 根据实际的业务情况来找到解决方案。
5. 流程图
