事件分发机制

基本概述

需要关注下是View还是ViewGroup,但整体上是一致的。

事件分发机制主要解决的核心问题是,将Touch事件,传递对应的view消费

  • 如何传递
  • 传递给谁

我们学习事件分发机制解决什么问题:

  • 如何利用机制去拦截目标事件
  • 如何利用机制去处理事件冲突

整体原则

  • 传递时,从外向内、从父到子
  • 消费时,从内向外、从子到父

三座大山

整个机制三个关键函数

  • dispatchTouchEvent, 事件传递
    • 返回true,表明事件已经消费,会终止事件向下(由父向子)传递,结束
    • 返回false,表明事件未被消费,将会回传给父View的onTouchEvent
    • 调用super.dispatchTouchEvent,继续下传
  • onInterceptTouchEvent
    • 这个方法只存在于 ViewGroup 中
    • 返回true,表明要拦截事件,会回调onTouchEvent
    • 返回false,表明不做拦截,后面会继续下传
  • onTouchEvent, 事件消费
    • 返回true,表明事件被消费,结束
    • 返回false,表明事件没有被消费,将会继续回传父view
    • 调用super.onTouchEvent,继续往上回传

事件分发路径图

在默认的实现下,事件流向是一个U型图。

需要注意的是,

关键点

一般情况下,有以下事件:

  1. ACTION_DOWN
  2. ACTION_MOVE
  3. ACTION_UP
  4. ACTION_CANCEL

KEY 1

而我们说的一个事件就是指上面的某一个,但若说一个完整事件序列,则是从down--move--...--cancel/up这样一个所有一个操作周期所发生的所有事件。

KEY 2

不同的事件的分发路径其实是有所差别的。

一旦前面的事件确定了接收的view,那么分发的U形图就会"向上收缩"。 例如下图被ViewGroup2接收后,后续事件在默认情况下,如红线只会走到VG2然后被处理,蓝色区域不会走了。

KEY 3

CANCEL事件会稍微特殊一点,发生在后续事件被其他view拦截的时候,原来接收事件的view就会收到CANCEL事件。

例如,Down事件被下层消费之后,后续的Move、Up事件是有可能会被上层拦截的这也正是常常发生事件冲突的地方。

若被拦截,则原本应该消费的View会收到cancel事件。

滑动冲突的处理方式

滑动冲突是非常常见的,通常发生在多个可滑动的布局嵌套的场景,例如RecyclerView{ ScrollView }, RecyclerView{ ListView }, ViewPager{ RecyclerView }

典型的,根据两个控件的滑动方向,可以将滑动冲突分成两类:一个是不同方向的滑动冲突 ,如外层控件垂直滑动,内层控件水平滑动。另一个就是同方向的滑动冲突,如内外两层控件都是垂直滑动。

拦截方法有两种,本质也是万变不离其宗(改变分发的路线):

  • 外部拦截法
  • 内部拦截法

在一些特殊业务场景下,可能需要内外一起完成配合来实现特殊的效果。

(1)外部拦截法

在外层ViewGroup中的onInterceptTouchEvent中,对move事件进行精细化控制。

例如在外部是纵向、内部是横向的滚动场景时,我们判断dy>dx时【实际条件根据业务场景设置】选择拦截即在onInterceptTouchEvent中return true.

(2)内部拦截法

在内部ViewGroup中的dispatchTouchEvent或onTouchEvent方法中,调用api requestDisallowInterceptTouchEvent(true)来阻止父布局拦截后续事件。

这时候内部ViewGroup是不会回调onInterceptTouchEvent的,所以需要在dispatchTouchEvent或onTouchEvent方法中调用。

例如在外部是纵向、内部是横向的滚动场景时,我们判断dx>dy时【实际条件根据业务场景设置】

设置requestDisallowInterceptTouchEvent(true)

做滑动布局,应该在哪拦截事件?

首先滑动布局是ViewGroup类型,里面存在子View。

1)明确一点,滑动事件肯定是在ViewGroup层去拦截;

2)滑动事件拦截,不应该以Down为起点,而应该是MOVE。否则:子View的点击事件也会被拦截

3)Move事件应该在onInterceptTouchEvent中拦截

你可参考

juejin.cn/post/716844...

相关推荐
岁岁种桃花儿5 分钟前
Vue核心语法第一篇:Vue是什么?
前端·javascript·vue.js
观无1 小时前
若依EasyExcel实现单元格合并
开发语言·前端·javascript
愚公搬代码1 小时前
【愚公系列】《Web应用安全》001-VMware的安装
前端·安全
xiaohaiAIgeo1 小时前
【2026年】HG/T 20656-2024化工暖通空调设计规范:新版标准的变化与影响
java·前端·javascript·科普知识
立少→万能汉编1 小时前
用“立少→超文本”写静态网页,标签<倍>
服务器·前端·javascript
weixin_431600441 小时前
NestJS 入门(7):生命周期钩子——构造函数和 `OnModuleInit` 差在哪?
前端·后端·学习·node.js·nest.js
你挚爱的强哥2 小时前
【chromeDownload】自定义组件:多用于登录框底部提供下载Chrome谷歌浏览器的链接
前端·vue.js·chrome
cindershade2 小时前
用原生 HTML5 视频与 Canvas 实现浏览器端视频抽帧封面
前端
笨鸟先飞,勤能补拙2 小时前
Web 服务器与计算机网络全景原理
运维·服务器·前端·网络·人工智能·计算机网络·web安全
NutShell Wang2 小时前
Wails v3 Beta 实战:用显式对象模型重写你的第一个 Go 桌面应用
前端·人工智能·go·vibe coding