Flutter 小技巧之快速理解手势逻辑

news2024/11/24 7:24:26

又到了小技巧系列更新时间,今天我们主要分享 Flutter 里的手势触摸逻辑,其实在很久之前我就写过 《面深入触摸和滑动原理》相关的源码分析文章,但是最近有人说源码分析看不懂,有没有简要好理解的,那么本篇就用更简单的角度,带大家理解 Flutter 里的手势相关逻辑

GestureDetector

不管你用 InkWellInkResponseTextButton 还是 ElevatedButton , 它们针对手势的处理逻辑都是来自于 GestureDetector ,也就是理解 Flutter 的手势处理逻辑入门,核心可以从分析 GestureDetector 开始。

其实更严格意义上讲,手势事件是来自 ListenerGestureDetector 是针对 Listener 进行了封装,只是为了避免复杂的源码分析,这里就不做展开,你可以简单理解为:并不是所有的控件都会响应手势,只有带有 Listener 的才会响应,这主要体现在触摸事件的递归响应上。

GestureDetector 里关于事件的响应逻辑主要来自于各种 GestureRecognizer (手势识别)的实现逻辑,不同的手势识别逻辑会响应不同手势结果,相互竞争,最后在 GestureArenaManager (竞技场) 决定出一个胜利者。

简单来说,在竞技场里手势基本遵循两个逻辑:

  • 每个 Recognizer 都可以随时选择失败退出,当竞技场只有它一个的时候它就赢了
  • 每个 Recognizer 都可以随时宣布自己获得胜利,这时其他 Recognizer 也将不再响应

那么如下图所示,在 GestureDetector 里主要有这 8 种 GestureRecognizer 在处理不同的场景,他们会根据用户使用 GestureDetector 时的参数搭配来参与到事件竞技场里。

举个例子,当你使用了 GestureDetector 并配置了 onTaponLongPressonDoubleTap ,它们是如何分别响应手势事件的?

这里的核心逻辑就在于 deadline (时间) 的处理,不管是 onLongPress 还是 onDoubleTap 都是靠 deadline 来判断胜负

例如,当用户只是普通点击时,如下代码所示,因为默认 LongPressGestureRecognizer 的 deadline 是 500 毫秒,所以在定时器达到 500ms 响应之前,就会因为 PointerUpEvent 导致长按定时器停止,无法触发响应长按事件

反之如果没有 PointerUpEvent 产生,那么 500 ms 之后 LongPressGestureRecognizer 就会响应,直接宣布胜利(accepted)。

默认情况下 GestureDetector 是不支持修改 deadline ,只有直接使用 LongPressGestureRecognizer 时才可以修改 deadline 的时长。

类似的逻辑在 DoubleTapGestureRecognizer 下也有,DoubleTap 的 deadline 是 300 毫秒,当用户首次点击时会注册一个定时器,如果 300 毫秒以内用户没有产生新的点击,那么 DoubleTapGestureRecognizer 就会宣布“失败“退出竞技,反之如果在 300 毫秒内有新的点击,则直接宣布“获胜”,响应 DoubleTap 回调。

那这时候有人就要问了:“DoubleTap 过程中,为什么不会触发 onTap” ? 这就需要说到 TapGestureRecognizer 的触发逻辑。

继续前面 GestureDetector 并配置了 onTaponLongPressonDoubleTap 的例子,在用户只做普通点击的时候,前面说过:

  • LongPressGestureRecognizer 的定时器 deadline 还没到 500 毫秒会因为 Up 事件而导致失败退出
  • DoubleTapGestureRecognizer 会因为定时器超过 deadline 300 毫秒,没有下一个点击而宣布退出

那么在 Long 和 Double 都失败的情况下,此时 GestureArenaManager (竞技场) 里的成员就只有 TapGestureRecognizer ,这时候竞技场会 close ,会触发竞技场的 sweep 逻辑,直接让最后剩下来的 Recognizer “胜利”,响应 onTap 事件。

所以 TapGestureRecognizer 靠的是胜者为王。

所以基于这个例子,配合一开始说的两个逻辑,就可以直观的理解 Flutter 手势竞技场里的响应逻辑和关键 deadline 的作用。

多个 GestureDetector

那么前面都是只有一个 GestureDetector 的场景,如果有两个呢?如下代码所示,在嵌套两个 GestureDetector 下,它们的响应逻辑会是怎么样的?

当区域内有两个 GestureDetector 的时候,用户在普通点击时,因为 deadline 影响,依旧会是在竞技场 close 时才响应 onTap但是不同在于此时竞技场里还会有多个 Recognizer 存在,这时候只有排在列表的第一个的 Recognizer 可以赢得事件,也就是上门代码里的红色 200x200 小方块。

因为对于多个 GestureDetector 的情况, Recognizer 在竞技场列表(List<GestureArenaMember)里的顺序和 HitTest 时的递归调用有关系,简单说就是:递归调用会就让我们自下而上的得到一个 HitTestResult 列表,代码里最后的 child 会在最上面

同时对于单个 GestureDetector 而言,TapGestureRecognizer 会是 _recognizers 的第一个,所以 first 会是响应了 TapGestureRecognizer ,详细逻辑可以看 《面深入触摸和滑动原理》 。

所以简单理解:

  • 两个 GestureDetector 在竞技场里的 member 列表排序时,作为 child 的红色 GestureDetector 因为 HitTest 递归会被排前面
  • GestureDetector 内部 TapGestureRecognizer 会在其内部 _recognizers 排第一

所以 member.first 最终响应了 TapGestureRecognizer ,回到上面两个定律,如果结合多个 GestureDetector 的场景,就应该是:

  • 每个 Recognizer 都可以随时选择失败退出,当竞技场只有它一个的时候它就赢了;如果不止一个,那么在竞技场 close 时, member.first 会获得响应
  • 每个 Recognizer 都可以随时宣布自己获得胜利,这是其他 Recognizer 也将不再响应

进阶补充

前面简单介绍了 Flutter 的手势响应的基础逻辑,这里再额外补充两个知识点。

首先,当用户在长按的时候, GestureDetector 何时会发出 onTapDown 事件

这其实就涉及了另外一个 deadline 参数,当用户在长按的时候,Recognizer 还会触发另外一个定时器,然后通过执行 didExceedDeadline 来发出 onTapDown 事件。

那么问题又来了,既然长按会触发 onTapDown 事件,如果点击区域内有两个 TapGestureRecognizer ,长按的时候因为定时器都触发了 didExceedDeadline ,那是不是两个都会收到 onTapDown 事件 ?

答案是:会的!因为定时器都触发了 didExceedDeadline,从而都发出了 onTapDown 事件,所以两个 onTapDown 回调都会执行,但是后续竞争只会有一个控件能响应 onLongPress

另外,如果不是长按导致的 Down 事件, 是不会导致两个 GestureDetector 都触发回调 onTapDown 回调。

第二个补充的是 Listener , 如果你还想深入去看 GestureDetector 的实现,你就会发现 GestureDetectorListener 的封装也许和你想象不大一样, 因为 Listener 的封装只用到了 PointerDown ,并没有用到 onPointerUp ,那 GestureDetector 是怎么响应 Up 和 Move 事件?

这就需要说到前面介绍 《面深入触摸和滑动原理》 里的源码分析,但是为了简单,我们这里只说结论:

因为只有响应了 PointerDown 事件,对应的 GestureRecognizer 才能被添加到 GestureBindingPointerRouter 事件路由和 GestureArenaManager 事件竞技中,而后续的 Up 和 Move 事件主要是通过 GestureBinding 来处理

更简单的说,就是只有响应了 PointerDown 事件,控件的 Recognizer 才能响应后续统一处理的其他手势事件,而其他事件不需要在 Listener 这里获取回调

结束

那本篇的小技巧到这里就结束了,本篇主要是用更直观和简单的方式,帮助大家理解 Flutter 里的触摸响应逻辑,如果对更详细实现感兴趣,可以结合 《面深入触摸和滑动原理》 帮助理解,如果你还有什么感兴趣或者有疑惑的,欢迎留言评论~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91351.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]Node.js计算机毕业设计高校图书馆网站Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

高通平台 5G RF调试总结

目录&#xff1a; 1.QRCT4的使用 2.RFC配置 3.5G CA 配置概括 4.RFPD 运行及错误分析 5.CA吞吐率问题分析 最新的5G HImalyaa平台RFC的配置方法和之前的平台发生了根本性的变化&#xff0c;主要体现在使用QRCT4工具来配置RFC XML文件&#xff0c;然后根据XML文件编译生成s…

MobileNetV3原理说明及实践落地

本文参考&#xff1a; pytorch实现并训练MobileNetV3 - 灰信网&#xff08;软件开发博客聚合&#xff09; 【神经网络】(16) MobileNetV3 代码复现&#xff0c;网络解析&#xff0c;附Tensorflow完整代码 - 代码天地 1 MobileNetV3与V1、V2对比 &#xff08;1&#xff09;Mob…

【LeetCode每日一题:1945. 字符串转化后的各位数字之和~~~模拟】

题目描述 给你一个由小写字母组成的字符串 s &#xff0c;以及一个整数 k 。 首先&#xff0c;用字母在字母表中的位置替换该字母&#xff0c;将 s 转化 为一个整数&#xff08;也就是&#xff0c;‘a’ 用 1 替换&#xff0c;‘b’ 用 2 替换&#xff0c;… ‘z’ 用 26 替换…

匿名浏览器是什么?为什么联盟营销需要借助匿名浏览器?

这段时间小伙伴们都对联盟营销很感兴趣&#xff0c;东哥也是陆陆续续出了两三篇相关的科普文章&#xff0c;今天继续给大家介绍匿名浏览器在联盟营销上的帮助&#xff0c;毕竟互联网时代&#xff0c;学会如何借助工具高效工作是很重要的。关于联盟营销的概念科普文章大家可以看…

学不会的python之通过某几个关键字排序、分组一个字典列表(列表中嵌套字典)

通过某个关键字排序、分组一个字典列表排序问题描述解决方案1.operator 模块的 itemgetter 函数2.lambda 表达式引申分组问题描述解决方案1.itertools.groupby() 函数2.defaultdict() 构建多值字典排序 问题描述 现在你有一个字典列表(列表中嵌套字典)&#xff0c;你想要根据…

web 向 unity 传输文件流 blob 记录

场景&#xff1a;web 与unity 通信&#xff0c;向 unity 传输文件 二进制流。 由 unity 转换并下载文件。 流程&#xff1a; web 端将缓存的 blob 数据流读取为 base64 编码的数据 → 传给 unity, →unity 解码转换 base64 数据并下载。 web 端&#xff1a; 1、 将数据转换成…

【Axure教程】自定义审批流原型模板

审批流即审批流程&#xff0c;是对某项工作的审批活动的一系列有序组合。审批流在业务系统中担当者非常重要的角色&#xff0c;所以今天作者就教大家制作一个通用的自定也审批流的原型模板&#xff0c;方便大家日后的工作。 一、效果展示 1、可以根据业务需要添加多个审批节点…

QT学习笔记(中)

QT学习笔记&#xff08;中&#xff09; 文章目录QT学习笔记&#xff08;中&#xff09;P21 消息对话框P22 其他标准对话框P23 登录窗口界面和布局P24 控件 按钮组P25 QListWidget控件P26 QTreeWidget控件的使用P27 tableWidgetP28 其他常用控件介绍P30 自定义控件P31 QEventP32…

PyQt5 QtChart-折线图

PyQt5 QtChart-QLineSeries 折线图QLineSeriesQLineSeries QLineSeries类将数据序列显示为折线图&#xff0c;其核心代码&#xff1a; lineSeries QLineSeries() lineSeries.append(1, 3) lineSeries.append(5, 8) … chart.addSeries(lineSeries) 常用方法&#xff1a; set…

【linux】容器之代码自动发布-docker

一、分析 旧&#xff1a; 代码发布环境提前准备&#xff0c;以主机为颗粒度静态 新&#xff1a; 代码发布环境 多套&#xff0c;以容器为颗粒度编译 二、业务发布逻辑设计图 三、工具使用流程图 工具 gitgitlabjenkinstomcatmavenharbordocker 流程图 四、主机规划 五…

​智能化加速,「中国供应商」如何跨越规模化周期|高工观察

在过去的十年时间里&#xff0c;中国在智能电动汽车行业下了巨大的「赌注」&#xff0c;整个行业及其背后快速成长的本地化产业链生态系统成为新一轮汽车产业增长的新引擎。 与此同时&#xff0c;电动化、智能化技术的国产化突围&#xff0c;也让整个中国本土汽车产业链获得了…

SuperMap GIS的TIN地形数据处理QA

目录 一、TIN地形数据简介 二、TIN地形数据格式 三、TIN地形数据处理 3.1 导入数据集 3.2 生成TIN地形缓存 3.3 IDesktop场景加载TIN地形 3.4 发布服务 3.5 WebGL场景加载 3.5.1 viewer初始化加载 3.5.2 scene.open加载 四、可能遇到的报错及解决方案 问题一&#xff1a;多个TI…

蓝海创意云×可米酷 || “360VR全景直播解决方案”亮相企业产品发布会

12月8日&#xff0c;可米酷2023新品发布会重磅召开&#xff0c;蓝海创意云为可米酷提供了前沿技术支持&#xff0c;助力整场活动实现了360全景VR在线直播&#xff0c;为企业线下发布会直播活动提供借鉴。 发布会现场采用了全新的虚拟现实技术VR视频全景直播方式&#xff0c;全国…

Spring 中 PageHelper 不生效问题

使用这个插件时要注意版本的问题&#xff0c;不同的版本可能 PageHelper 不会生效 springboot 导入的 pagehelper 包 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><vers…

java+mysql 基于ssm的校园二手交易系统

现如今,校园二手交易系统是商业贸易中的一条非常重要的道路,可以把其从传统的实体模式中解放中来,网上购物可以为消费者提供巨大的便利。通过校园二手交易系统这个平台,可以使用户足不出户就可以了解现今的流行趋势和丰富的商品信息,为用户提供了极大的方便,校园二手交易系统的…

技术分享 | 跨平台API对接(Java)

本章介绍基于 Jenkins API 调用的跨平台 API 对接。 基于Jenkins实现跨平台API对接 Jenkins 提供了远程访问应用编程接口&#xff08;Remote Access API&#xff09;&#xff0c;能够通过 Http 协议远程调用相关命令操作 Jenkins 进行 Jenkins 视图、任务、插件、构建信息、任…

vue3 安装使用scss

1、安装相关依赖 node-sass css-loader style-loader sass-loader 2、声明 lang"scss" 或者 scss文件中就可以直接使用 3、重点&#xff1a;安装依赖的过程中出现的各种问题 3.1、安装node-sass 报错 如果没有安装python,就去下个安装包装一下记得配置环境变量…

世界杯小吐槽

冷门 在看这次世界杯的时候&#xff0c;心里真的是一上一下&#xff0c;今年的冷门太多了&#xff01; 如&#xff1a; 阿根延 VS 沙特阿拉伯 阿根延输了&#xff08;我想可能是阿拉伯的战术比较新吧!&#xff09;那场比赛之后&#xff0c;阿拉伯还全国放假一天。到现在&#…

1.浮动 float

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 1.4什么是浮动 float属性用于创建浮动框&#xff0c;将其移动到右边&#xff0c;直到左边缘或右边缘触及包含块或另一个浮动框的边缘。 1、语法&#xff1a; <style> …