Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2019-04-22:谈谈Android的事件分发机制? #35

Open
Moosphan opened this issue Apr 22, 2019 · 17 comments
Open

2019-04-22:谈谈Android的事件分发机制? #35

Moosphan opened this issue Apr 22, 2019 · 17 comments

Comments

@Moosphan
Copy link
Owner

No description provided.

@SuDreamer

This comment has been minimized.

@429329513wanting

This comment has been minimized.

@zwonb
Copy link

zwonb commented Apr 22, 2019

当点击的时候,会先调用顶级viewgroup的dispatchTouchEvent,如果顶级的viewgroup拦截了此事件(onInterceptTouchEvent返回true),则此事件序列由顶级viewgroup处理。如果顶级viewgroup设置setOnTouchListener,则会回调接口中的onTouch,此时顶级的viewgroup中的onTouchEvent不再回调,如果不设置setOnTouchListener则onTouchEvent会回调。如果顶级viewgroup设置setOnClickListener,则会回调接口中的onClick。如果顶级viewgroup不拦截事件,事件就会向下传递给他的子view,然后子view就会调用它的dispatchTouchEvent方法。

@ADrunkenLiBai

This comment has been minimized.

@StefanShan
Copy link

StefanShan commented Apr 22, 2019

1.触发过程:Activity->Window->DocerView->ViewGroup->View,View不触发再返回由父级处理依次向上推。
2.在每个阶段都要经过三个方法 dispatchTouchEvent(分发)、onInterceptTouchEvent(拦截)、onTouch(处理)

大体流程:
Activity中走了Window 的 dispatch,Window 的 dispatch 方法直接走了 DocerView 的 dispatch 方法,DocerView 又直接分发给了 ViewGroup,ViewGroup 中走的是 onInterce 判断是否拦截,拦截的话会走 onTouch 来处理,不拦截则继续下发给 View。到 View 这里已经是最底层了,View 若继续不处理,那就调用上层的 onTouch 处理,上层不处理继续往上推。

推荐一篇介绍非常详细的博客:https://www.jianshu.com/p/38015afcdb58

@MoJieBlog
Copy link
Collaborator

给大家举个形象的例子吧。ViewGroup相当于老板或者部门管理。View相当于员工。onTouchEvent相当于接业务。dispatechTouchEvent相当于处理任务。onIterceptTouchEvent相当于拦截任务。
先来分配角色

  • ViewGroup老板,或者部门管理有接收任务(onTouchEvent),处理任务(dispatchTouchEvent),拦截任务(oninterceptTouchEvent)的方法
  • View有接收任务(onTouchEvent),处理任务(dispatchTouchEvent)的方法。

现在模拟来业务了(点击事件)
老板(ViewGroup)接到业务(onTouchEvent),他有两个选择

  • 这个任务太轻松了,没必要往下传递我直接自己处理,所以拦截任务(oninterceptTouchEvent),并告诉客户处理结果
  • 这个任务我不擅长,但是X部门比较擅长,我交给(dispatechTouchEvent)X部门
    • 然后X部门领导接到任务,部门管理者有两个选择

      • 自己处理oninterceptTouchEvent,并且告诉老板(返回true),我已经处理了
      • 交给部门其他员工(子View)处理
        • 员工接到任务(onTouchEvent),员工只能处理任务(dispatchTouchEvent)。并把处理结果告诉领导(return true|renturn false)。
    • 领导接到员工的反馈,告诉老板(return true|renturn false)。

  • 老板告诉客服处理结果

@futureyang
Copy link

责任链模式

@18361237136
Copy link

18361237136 commented Apr 22, 2019

  1. 会经过Activity->ViewGroup->view,一次往下传递事件,如果一直不拦截再回调回来。
  2. 主要经过三个方法,dispatchTouchEvent(分发事件),oninterceptTouchEvent(是否拦截View中不存在),onTouchEvent(处理)。
  3. 三个方法的用法是,先用dispatchTouchEvent来分发事件,然后用oninterceptTouchEvent来判断是否拦截该任务(此方法在dispatchTouchEvent内部),如果不拦截直接dispatch向下回调,如果拦截就调用自己的onTouchEvent来处理事件。
  4. 如果由setOnClickListener方法会先执行onClick.

@qianxin2016
Copy link

事件来源:input -> window -> ViewRoot -> DecorView -> Activity -> ...
事件处理:责任链模式,先从顶层传到底层,再从底层传回顶层,如果中间被消费或拦截,则流程结束
从Activity.dispatchTouchEvent()开始
--向下--> ViewGroup.dispatchTouchEvent(),可通过onInterceptTouchEvent()拦截
--向下--> View.dispatchTouchEvent()
--向下--> View.onTouchEvent()
--向上--> ViewGroup.onTouchEvent()
--向上--> Activity.onTouchEvent()
注意点:如果View没有消费ACTION_DOWN,后面的其他事件就不会再传过来

@Techvisionbest
Copy link

1.触发过程:Activity->Window->DocerView->ViewGroup->View,View不触发再返回由父级处理依次向上推。
2.在每个阶段都要经过三个方法 dispatchTouchEvent(分发)、onInterceptTouchEvent(拦截)、onTouch(处理)

大体流程:
Activity中走了Window 的 dispatch,Window 的 dispatch 方法直接走了 DocerView 的 dispatch 方法,DocerView 又直接分发给了 ViewGroup,ViewGroup 中走的是 onInterce 判断是否拦截,拦截的话会走 onTouch 来处理,不拦截则继续下发给 View。到 View 这里已经是最底层了,View 若继续不处理,那就调用上层的 onTouch 处理,上层不处理继续往上推。

推荐一篇介绍非常详细的博客:https://www.jianshu.com/p/38015afcdb58

View是没有onInterceptTouchEvent(拦截)的方法的

@maoqitian
Copy link

之前写过一篇文章回顾时间分发机制,希望能有补充。
深入理解Android事件分发机制

@chenqi5256969
Copy link

1.当我们按下手指,ViewGroup的onInterceptTouchEvent会最先处理down事件。
假设1:onInterceptTouchEvent返回false,子view的onTouchEvent返回true,后续的move和up事件都将传到onInterceptTouchEvent中,然后一层一层的传递下去交给子view处理
假设2:onInterceptTouchEvent返回false,子view的onTouchEvent返回的是false,则后续的move和up事件都将交给ViewGroup的onTouchEvent处理,如果ViewGroup的onTouchEvent返回为true,ViewGroup将会自己处理,如果ViewGroup的onTouchEvent返回false,则会继续向上传递,交给ViewGroup的父亲来处理。
假设3:onInterceptTouchEvent返回true,那么down和后续的move和up事件将会交给ViewGroup的onTouchEvent处理

@devzhan
Copy link

devzhan commented Apr 26, 2020

当顶级的ViewGroup接收到事件消息之后,会调用dispatchTouchEvent 方法进行分发事件,
在分发时会先去判断该事件是否拦截onInterceptTouchEvent。如拦截,则事件不继续进行
分发,自己进行消费。如不拦截,事件继续进行向下传递到子控件,如果子控件是ViewGroup
则继续走上述操作,如果是View则会走onTouchEvent方法。
其中ViewGroup不拦截之后会先走onTouch方法,该方法会影响其onTouchEvent。整体传递是一个责任链模式

@qweenhool
Copy link

qweenhool commented Aug 28, 2021

事件分发的细节真的非常多,用老板员工的例子,最早开始看的时候我是没记住过。如果你只是看别人得出来的结论,那么十个人他能总结出十种自己的结论,看似好像明白了,过一段时间自己去看还是会忘记,所以我觉得想要真的明白就得自己去跟踪源码,然后用关键代码提炼成伪代码,打log看结果,做好笔记。由于你没法获得自己手机的系统源码,所以最好用模拟器来调试,用模拟器对应的sdk版本编译运行代码,自定义几个简单的View和ViewGroup,从ViewGroup的dispatchTouchEvent()方法处开始打断点。这里分享一个小技巧,如果你想要程序在执行到你自定义ViewGroup的dispatchTouchEvent()处暂停,可以在断点的condition处加上this.getClass().getSimpleName().equals("CustomViewGroup1");这样的条件语句。后续总结完毕,我会发文章出来给大家参考的,大家有问题可以互相交流,共同进步。

@mlinqirong
Copy link

mlinqirong commented Dec 17, 2021

事件分发机制是责任链模式,先从顶层传到底层,再从底层传回顶层,如果中间被消费或拦截,则流程结束:
Activity dispatchTouchEvent- >ViewGroup dispatchTouchEvent ->ViewGroup onInterceptTouchEventview->View dispatchTouchEvent -> ->View onTouchEvent-> ViewGroup onTouchEvent->Activity onTouchEvent
事件监听分别有
dispatchTouchEvent(分发)onInterceptTouchEvent(拦截)onTouchEvent(响应)事件
Activity和view只有dispatchTouchEvent(分发)onTouchEvent(响应)事件
因为View没有子View,所以不需要拦截事件。而ViewGroup里面可以包裹子View,所以通过onInterceptTouchEvent方法
在dispatchTouchEvent中调用super.dispatchTouchEvent有底层执行底层dispatchTouchEvent 没有底层执行本层的onTouchEvent响应事件 dispatchTouchEvent返回true则表示响应事件不上发 返回false继续上发执行顶层onTouchEvent

@yline
Copy link

yline commented Sep 1, 2023

① 驱动层 -> Framework 层 : 用户触摸 , 或按键 后 , 事件在硬件中产生 , 从 硬件驱动层 , 传递到 Framework 层 ;

② WMS -> View 层 : WindowManagerService ( 简称 WMS ) 将事件传递到 View 层 ;

③ View 层内部 : 事件在 View 的容器及下层容器 / 组件 之间传递 ;

https://cloud.tencent.com/developer/article/1154110

@flyisme
Copy link

flyisme commented Jun 20, 2024

伪代码: 单指操作下的:

// ViewGroup 的 dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    
    // DOWN 事件重置触摸状态
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        mFirstTouchTarget = null; 
    }

    boolean intercept = onInterceptTouchEvent(ev); // 拦截事件

    if (!intercept) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            // 查找 mFirstTouchTarget
            for (View child : childs) {
                if (mFirstTouchTarget!=null&&child.pointInView(ev.getX(), ev.getY()) && child.dispatchTouchEvent(ev)) {
                    // 初始化 mFirstTouchTarget 链表,并添加 child
                    mFirstTouchTarget = new TouchTarget(child);
                    break;
                }
            }
        }
    }

    if (intercept || mFirstTouchTarget == null) {
        consume = super.dispatchTouchEvent(ev); // 最终调用View.dispatchTouchEvent
    } else {
        consume = mFirstTouchTarget.child.dispatchTouchEvent(ev); // 分发给 mFirstTouchTarget
    }

    return consume;
}

// View 的 dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;

    if (this.mOnTouchListener != null) {
        consume = mOnTouchListener.onTouch(this, ev);
    } 

    if (!consume) {
        consume = onTouchEvent(ev);
    }

    return consume;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests