Android 事件分发与滑动冲突 面试知识点

整理自模拟面试,覆盖触摸反馈 / 事件机制 / 事件分发 / ViewRootImpl / requestDisallowInterceptTouchEvent / 滑动冲突


1. 什么是触摸反馈

一句话:触摸反馈就是用户手指按下后,界面立即给出的视觉、状态或触觉响应,用来告诉用户“这次操作已经被接收了”。

常见反馈形式

核心价值

实现要点


2. Android 事件机制总览

一句话:Android 触摸事件本质是 MotionEvent,核心就是事件从上到下分发,再由某一层消费。

常见事件类型

三个核心方法

一句话速记


3. 事件分发详细流程

View 层经典链路

Activity
-> Window
-> DecorView
-> ViewGroup
-> 子 View

更完整的系统链路

InputDispatcher
-> ViewRootImpl
-> DecorView
-> ViewGroup
-> 子 View

分发过程怎么理解

  1. 系统生成 MotionEvent
  2. 事件先进入 Activity.dispatchTouchEvent()
  3. 再经由 Window.superDispatchTouchEvent() 传给 DecorView
  4. 进入根 ViewGroup.dispatchTouchEvent()
  5. 父容器决定是否拦截
  6. 不拦截则向命中的子 View 继续分发
  7. 子 View 或父容器最终在 onTouchEvent() 中消费

ViewGroup.dispatchTouchEvent() 的核心逻辑


4. 为什么 ACTION_DOWN 很重要

一句话ACTION_DOWN 是一整组触摸事件的起点,谁接住了 DOWN,后续事件通常就优先发给谁。

DOWN 的作用

子 View 不消费 ACTION_DOWN 会怎样

为什么父容器通常不能随便拦截 ACTION_DOWN


5. MOVE 阶段拦截是怎么发生的

一句话:父容器通常是在 onInterceptTouchEvent() 里,根据滑动方向、距离和边界,在 MOVE 阶段动态决定是否接管事件。

典型流程

常见判断条件


6. OnTouchListenerOnClickListener 的关系

结论OnTouchListener 不会天然让点击失效,关键看 onTouch() 的返回值。

两种情况

原因

为什么有些 View 没重写 onTouchEvent() 也能点击


7. ViewRootImpl 和事件分发是什么关系

一句话ViewRootImpl 不负责父子 View 之间的拦截决策,但它负责把系统输入事件接进当前窗口的根 View。

它的角色

在事件机制里的位置

结论


8. 滑动冲突怎么处理

一句话:滑动冲突本质上是父子控件都想消费同一组事件,核心是尽早明确“这次手势到底归谁处理”。

常见场景

常见方案

1. 外部拦截法

2. 内部拦截法

常用判断依据

实战建议


9. requestDisallowInterceptTouchEvent() 的作用和调用时机

一句话:子 View 用它通知父容器“当前这组事件后续先别拦截我”。

最常见调用时机

  1. ACTION_DOWN
  2. 先预防性调用一次 requestDisallowInterceptTouchEvent(true)
  3. 告诉父容器先不要太早接管

  4. ACTION_MOVE

  5. 根据方向、距离、边界动态调整
  6. 确认子 View 要继续处理时,保持 true
  7. 确认应该让父容器处理时,改成 false

需要注意


10. 源码里父容器什么时候判断 requestDisallowInterceptTouchEvent

结论:父 ViewGroup 是在 dispatchTouchEvent() 过程中判断这个标记的。

底层思路

可以这样理解

if (disallowIntercept) {
    intercepted = false
} else {
    intercepted = onInterceptTouchEvent(ev)
}

额外补充


11. 为什么 ACTION_DOWN 时这个标记会失效或被重置

一句话:因为 ACTION_DOWN 代表新一轮事件序列开始,父容器会重置上一次触摸分发状态。

重置的原因

常见会被重置的内容

面试表达


12. 高频面试一句话总结

知识点 一句话速记
触摸反馈 用户按下后立即给出的视觉或触觉响应,本质是提升操作确定性
事件机制 dispatchTouchEvent 负责传,onInterceptTouchEvent 负责截,onTouchEvent 负责处理
ACTION_DOWN 事件序列起点,谁消费了 DOWN,后续事件通常就归谁
MOVE 拦截 父容器通常在 onInterceptTouchEvent() 里动态判断是否接管
点击失效 常见原因是 DOWN 没消费、父容器拦截、OnTouchListener 提前吃掉事件
ViewRootImpl 负责把输入送进 View 树,也负责布局和绘制调度
滑动冲突 核心是根据方向、距离、边界尽早明确事件归属
requestDisallowInterceptTouchEvent 子 View 用来告诉父容器“这组事件后续先别拦截我”