外观
从手指触屏到 onClick,中间经过哪些环节?
标准回答
触屏硬件产生事件,内核输入驱动上报后,EventHub / InputReader 读取并转换成 Android 输入事件,InputDispatcher 再根据窗口与输入状态选择合适的目标窗口。事件通过输入通道进入应用,再沿应用的输入处理和 View 分发链路执行。
对普通 Activity 页面,可以先理解为 Activity 与窗口把事件交给 DecorView,然后由各级 ViewGroup 的 dispatchTouchEvent 继续分发。父容器可以通过 onInterceptTouchEvent 拦截;目标 View 中,满足调用条件的 OnTouchListener 通常先处理,未消费时再交给 onTouchEvent。
onClick 不是触屏驱动直接送来的事件,而是控件根据一段有效触摸序列识别出的点击行为,通常通过 performClick 触发。滑动取消、父容器拦截或监听器提前消费,都可能让这段操作不再构成点击。
因此按钮没反应,要逐层区分:事件是否进入目标窗口、是否被父容器截走、监听器是否消费,以及主线程是否来得及处理。不能只在 onClick 加日志,没有打印就判断系统丢事件。
关键机制
- DOWN / MOVE / UP / CANCEL 属于一段手势序列;是否消费 DOWN 对后续目标分配很重要。
- 父容器中途拦截时,原子目标通常收到 CANCEL,应清理按下、拖动等状态。
- 输入分发是跨进程链路,但不能把每一个高频触摸事件简单说成一次 Java AIDL 方法调用。
通俗例子
列表里的按钮 DOWN 已收到,但用户随后纵向滑动,列表拦截并取消按钮手势;没有 onClick 是符合预期的行为。
追问与回答
OnTouchListener 返回 true 为什么常导致 onClick 不执行?
返回 true 表示监听器消费了事件,默认 onTouchEvent 往往不再处理这次事件,因此默认点击识别流程没有完整运行。需要自定义点击时,应根据有效手势调用 performClick 并正确处理取消与无障碍,而不是无条件在每次 MOVE 中调用点击回调。
易混淆点
窗口焦点、触摸命中和可点击状态不是同一个概念;系统级手势还可能在进入普通控件前被处理。
参考资料
内容来源
- Android 技术札记 · 原条目
input-pipeline