事件分发在 android 中实在是太重要了,滑动冲突的前置基础知识,滑动冲突不会处理,那么大部分页面效果是写不出来的,尤其是时下越来越多的联合滑动的页面效果,典型的就是 behavior 了,其他的像是典型的滑动控件嵌套冲突,左滑退出,列表侧滑菜单,下拉刷新,上拉加载都是需要我们对 android 事件分发有深刻理解的。
事件分发谁来控制
android 的页面结构是这样的,典型的树形结构


每一个 Actvity 都有一个 window ,window 所有视图的最顶层容器,视图的外观和行为都归他管,不论是背景显示,标题栏还是事件处理都是他管理。
一般我们用不到去操作 window ,但是触控事件的接收和分发是由 window 来执行的,window 会根据视图树的顺序,从外向里层层传递触控事件
所以触控事件是由 window 接收并分发的。
事件分发4大方法
- dispatchTouchEvent 事件分发
- onInterceptTouchEvent 事件拦截
- onTouchEvent 事件处理
- requestDisallowInterceptTouchEvent(true)
ViewGroup 4个方法都有,View 没有事件拦截方法。这4个方法就是 Android 事件分发和处理的具体方法了,我们在实际处理时都是重写这4个方法。
这3个方法都有一个 boolean 的返回值,onInterceptTouchEvent 方法表示我要拦截这个事件,onTouchEvent 表示我处理了事件
-
dispatchTouchEvent
dispatchTouchEvent 方法作为 view 的事件处理的 API 入口,内部会调用 onInterceptTouchEvent 和 onTouchEvent 来计算返回的 boolean 值,一般我们都不会动 dispatchTouchEvent 方法,因为没有意思。但是我们直接返回 true 时,表示我消费这个事件了 -
onInterceptTouchEvent
onInterceptTouchEvent 方法默认是返回 false 的,表示不会拦截这个事件,但是若是返回 true 则表示这个事件我拦截住了,就不再往我下一级 view 传递了,然后会把事件交给自己的 onTouchEvent 方法 -
onTouchEvent
onTouchEvent 方法可以获取具体的触控参数,进行手势方向,当前手势类型判断,可以记录触摸点的 x,y 坐标。onTouchEvent 方法若是返回 true ,则表示这个事件我已经处理过了,那么整个事件传递过程就结速了,否则的话这个事件会原路返回,去一级一级的跑上一层 view 的 onTouchEvent 方法,直到有人返回 true 或是没有了。 -
requestDisallowInterceptTouchEvent
view.getParent().requestDisallowInterceptTouchEvent(true) ,可以让内层的 view 获取到事件,而忽略上层视图的拦截。true 表示接受事件,fasle 表示不接受事件。原理分析看这里:
事件分发的顺序
总的来说,事件是由其父视图向子视图传递,按前面的图来说是这样。
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View
- window 根据视图树顺序,从外到内遍历所有的 view ,询问每一个遍历出来的 view 你要不要消费触摸事件
- 每个 view 会跑自己的 dispatchTouchEvent 方法,判断自己是否需要消费这个事件
- dispatchTouchEvent 方法首先会询问 onInterceptTouchEvent 方法,是否要拦截这个事件。
- 若是拦截则会跑自己的 onTouchEvent 方法,若不拦截则会传递给下一级 view,调用下一级 view 的 dispatchTouchEvent 方法
- onTouchEvent 若是返回 true ,表示事件实际处理过了,那么整个事件传递在这个节点就结速了,不再传递了。若是返回 fasle,这个事件会原路返回,去一级一级的跑上一层 view 的 onTouchEvent 方法,直到有人返回 true 或是没有了。



这个点建议大家去看经典的文章
前人的经典文章我就不逾越了,推荐大家看经典,省的理解错误,这里我放地址
具体的例子我就不写了,大家看这篇文章的例子,设计的很全面
想了解的触控类型和参数还有源码分析的看这里
想更详细了解 MotionEvent 看这里,很详细包括多点触控
我复制一下核心要点:
- 事件分发原理: 责任链模式,事件层层传递,直到被消费。
- View 的 dispatchTouchEvent 主要用于调度自身的监听器和 onTouchEvent。
- View的事件的调度顺序是 onTouchListener > onTouchEvent > onLongClickListener > onClickListener 。
- 不论 View 自身是否注册点击事件,只要 View 是可点击的就会消费事件。
- 事件是否被消费由返回值决定,true 表示消费,false 表示不消费,与是否使用了事件无关。
- ViewGroup 中可能有多个 ChildView 时,将事件分配给包含点击位置的 ChildView。
- ViewGroup 和 ChildView 同时注册了事件监听器(onClick等),由 ChildView 消费。
- 一次触摸流程中产生事件应被同一 View 消费,全部接收或者全部拒绝。
- 只要接受 ACTION_DOWN 就意味着接受所有的事件,拒绝 ACTION_DOWN 则不会收到后内容。
- 如果当前正在处理的事件被上层 View 拦截,会收到一个 ACTION_CANCEL,后续事件不会再传递过来。
网友评论