Android事件分发的底层原理

在Android中,Touch事件的分发在WindowManagerService(借助 InputManagerService)负责采集和分发,在由ViewRootImpl(内部有一个mView变量指向View树的根),负责控制View树的UI绘制和事件消息分发。

当输入设备可用时,比如触屏,Linux内核在/dev/input/中创建对应的设备节点。

IMS(inputManagerService),所所做的工作就是监听/dev/input下的所有的设备节点,当设备节点的数据时会将数据进行加工处理并找到合适的Window(WMS寻找),将输入事件派发给他。

事件采集
  • 设备触摸
  • 触摸电信号
  • 电容屏
  • 传感器
  • 电路板
  • 驱动
  • Linux
  • IMS(inputManagerService

当输入设备可用时,比如触屏,Lunux内核会在/dev/input中创建对应的设备节点,输入事件所产生的原始信息会被Linux内核输入子系统采集,原始信息由Kernel Space的驱动层一直传递到User Space的接设备节点。

事件中转
  • WMS(WindowManagerService

WMS的职责之一就是输入系统的中转站,WMS(WindowManagerService)作为WIndow的管理者,会配合IMS将输入事件交给合适的Window来处理。

事件分发(分发)
  • ViewRootimpl

ViewRoot中 caiquWindowInputEventReceiver进行具体的事件处理

1:事件分发的基本概念

  • 触摸事件:Android的触摸事件主要封装在MotionEvent类中
  • 事件类型:包括ACTION_DOWNACTION_MOVEACTION_UPACTION_CANCEL等..
  • 分发流程:事件从Activity --> ViewGroup --> View的传递过程

2:三个核心方法

dispatchTouchEvent(MotionEvent ev)

  • 作用:负责事件的分发,决定事件是否继续传递

  • 返回值:true表示消费了事件,false表示不处理此事件

    • true/拦截:调用自身的onTounchEvent
    • false/不拦截:将事件传递给子View
  • 执行逻辑:通常先调用onInterceptTouchEvent判断是否拦截

onInterceptTouchEvent(MotionEvent ev)

  • 特性:仅ViewGroup拥有此方法(View没有)

  • 作用:ViewGroup特有方法,用于拦截事件

  • 返回值:true表示拦截事件,false表示不拦截(默认)

    • true/拦截:不再向子View传递
    • false/不拦截:默认值
  • 注意:View没有此方法,无法拦截事件

onTouchEvent(MotionEvent event)

  • 作用:处理点击事件的具体逻辑
  • 返回值:true表示消费事件,false表示不处理事件
  • 执行时机:当dispatchTouchEvent传递到当前View时被调用
  • 特殊情况:如果没有子View消费事件,最终会调用此方法

//事件分发机制流程

public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    if (onInterceptTouchEvent(ev)) { // 判断是否拦截
        // 拦截后调用自己的onTouchEvent
        consume = onTouchEvent(ev);
    } else {
        // 不拦截则分发给子View
        consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}

Activity的事件分发

  • 入口:事件首先传递给Activity的dispatchTouchEvent
  • 传递:Activity将事件传递给Window,再传递给DecorView
  • 最终:事件到达根ViewGroup开始向下分发

ViewGroup的事件分发特点

  • 遍历子View:按Z轴顺序遍历子View检查能否接受点击
  • 坐标转换:将事件坐标转换为子View的相对坐标
  • 优先处理:如果有子View设置了TouchListener,会优先处理

关键差异对比

函数 ViewGroup View
dispatchTouchEvent 决定是否分发给子View 直接处理事件
onInterceptTouchEvent 存在,可拦截事件 不存在
onTouchEvent 处理未被分发的事件 处理自身事件
事件分发流程
  1. Activity → Window → DecorView(ViewGroup)
  2. ViewGroup 调用 dispatchTouchEvent
  3. ViewGroup 调用 onInterceptTouchEvent 判断是否拦截
  4. 如不拦截,则传递给相应子View
  5. 子View调用自身的 dispatchTouchEvent 和 onTouchEvent
  6. 如拦截,则调用自身的 onTouchEvent

事件的内部拦截法和外部拦截法

一:外部拦截法(推荐)

基本原理:

  • 在父容器(ViewGroup)的 onInterceptTouchEvent方法中进行拦截控制
  • 通过判断滑动方向或条件来决定是否拦截事件
// 父容器实现
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    if (action == MotionEvent.ACTION_DOWN) {
        // DOWN事件不能拦截,否则后续事件都无法接收到
        return false;
    }
    
    // 根据滑动方向判断是否拦截
    if (needIntercept()) {
        return true;  // 拦截事件,父容器处理
    }
    return false;  // 不拦截,子View处理
}

private boolean needIntercept() {
    // 根据滑动距离、方向等条件判断
    return Math.abs(deltaX) > Math.abs(deltaY);
}

特点:

  • DOWN事件不能拦截:必须返回false,保证父容器能接收到后续事件
  • 控制简单:只需在父容器中处理拦截逻辑
  • 符合默认机制:与Android事件分发机制一致

二:内部拦截法

基本原理:

  • 子View通过 requestDisallowInterceptTouchEvent方法控制父容器是否拦截
  • 子View主动告知父容器不要拦截事件
// 子View实现
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            // 请求父容器不要拦截
            getParent().requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            if (isScrollToTop()) {
                // 如果滑动到顶部,允许父容器拦截
                getParent().requestDisallowInterceptTouchEvent(false);
            }
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            getParent().requestDisallowInterceptTouchEvent(false);
            break;
    }
    return super.dispatchTouchEvent(ev);
}

// 父容器默认不拦截除了DOWN以外的所有事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    if (action == MotionEvent.ACTION_DOWN) {
        return true;  // DOWN事件必须拦截
    }
    return false;   // 其他事件默认不拦截
}

特点:

  • 需要协调配合:子View和父容器都需要参与处理
  • 灵活性高:子View可以更精确地控制事件处理
  • 复杂度较高:需要在多个地方进行处理
注意事项
  • DOWN事件处理:无论哪种方法,ACTION_DOWN 事件都不能被拦截(外部拦截法)或需要特殊处理(内部拦截法)
  • 事件完整性:同一事件序列(DOWN-MOVE-UP)应由同一个View处理
  • 性能考虑:避免在拦截判断中进行耗时操作
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容