本文原作者: 张风捷特烈,原‍文发布于: Juejin

https://juejin.cn/post/7057680571157184549

如果可以模拟 PointerEvent 进行分发,那么在应用中就可以通过代码来触发手势事件,这样就能解放双手。如果结合语音监听,通过代码处理,说话也能触发手势操作,岂不美哉。

作为探索完手势机制滑动机制,又有完成这两本小册的我,感觉这个问题应该可解。下面就将整个问题的解决过程进行梳理,带大家再认识一下手势底层的相关实现。

一、模拟按下事件

1. 思路分析 1

PointerEvent 作为手势机制中被传递的数据,它记录着触点的 id坐标触点类型等信息。所以如果有办法发送一个 PointerDownEvent 的消息,不就表示按下了吗?

2. 自己分发事件

然后想到手势事件分发是由 GestureBinding 处理的,而我们可以通过 GestureBinding.instance 获取 GestureBinding 对象。那是不是意味着,可以自己来分发一个 PointerDownEvent 的消息。于是创建如下示例界面: 上部有两个按钮分别用于模拟滑动模拟点击

我们现在的目标是通过模拟点击可以点击右下角的加号按钮,从而让上面黄色区域内的数字自加;通过模拟滑动让列表滑动。

于是写了如下 48 行的代码通过 GestureBinding 对象的 dispatchEvent 来分发事件:

现在问题来了,第二入参需要传入 HitTestResult 对象。

但它是一个可空的入参,所以传个 null 试试:

GestureBinding.instance!.dispatchEvent(p0, null);

很不出所料地,抛了异常,看来这样直接发送消息似乎并不是正解。那么来分析一下这样为何不可。

3. GestureBinding#dispatchEvent 的逻辑处理

下面通过调试来看一下 GestureBinding#dispatchEvent 的逻辑处理: 402 行表示,当 hitTestResult 为 null 时,当前的 event 对象类型必须是 PointerAddedEvent 和 PointerRemovedEvent。而我们上面传入 PointerDownEvent,使用肯定会抛异常。

所以现在的问题是,如果我们无法创建 HitTestResult,就无法通过 dispatchEvent 方法来分发 PointerDownEvent 事件。但 HitTestResult 是从 hitTest 收集的,我们似乎很难去主动创建,似乎问题进入了死胡同。

二、单击事件是如何触发的

1. 回顾单击事件的触发

如下是点击加好按钮时 FloatingActionButton#onPressed 回调触发的方法栈情况,可以看到是在分发 PointerUpEvent 类型事件下触发单击事件的:

其实这也很好理解: 一个单击事件的触发条件并非只是分发 PointerDownEvent 而已,TapGestureRecognizer 手势检测器至少需要按下、抬起才会被触发。

所以我们可以在 GestureBinding#dispatchEvent 分发方法打个断点,通过点击 + 按钮,看看有哪些 PointerDownEvent 会被分发。

2. 单击事件分发的 PointerEvent

如下所示,首先会分发 PointerAddEvent 事件,此时 hitTestResult 为 null,

接下来分发 PointerDownEvent 事件,可以看出此时 hitTestResult 就已经非空了,这说明在分发 PointerAddEvent 事件后,分发 PointerDownEvent 事件前,肯定有对 HitTestResult 进行收录的处理。

最后分发 PointerDownEvent 事件,然后就触发了单击事件的回调。

3. HitTestResult 的收集

那接下来看一下 PointerDownEvent 事件分发前,HitTestResult 是如何被收集的。其实想知道这点很简单,dispatchEvent 既然要传入 HitTestResult 对象,只要通过调试看一下这个对象的来源即可:

只要往下看两个方法栈,很容易定位到在 GestureBinding._handlePointerEventImmediately 方法中当 event 是 PointerDownEvent、PointerSignalEvent、PointerHoverEvent 时,都会创建 HitTestResult 对象,在通过 hitTest 方法来收集测试结果。

至于 hitTest 方法是如何从顶层的 RenderView 一层层测试的,这里就不展开了。感兴趣的可以自己调试看看。

其实这样一来,我们如果可以触发这个方法就好了,但可惜它是个私有成员方法。但我们眼睛可以稍微向下瞄一个方法栈,普通成员方法 GestureBinding.handlePointerEvent 可以触发这个私有方法。到这里,一个解决方案就应运而生了。

三、模拟事件触发的实现

如下效果所示: 通过模拟点击可以点击右下角的加号按钮,从而让上面黄色区域内的数字自加;通过模拟滑动让列表滑动。这样我们就实现了通过代码触发手势事件

1. 单击事件

其实我们只需要通过 GestureBinding#handlePointerEvent 依次分发这三个 PointerEvent,就能模拟单击事件的触发了。没错,就是这么简单,但其中涉及到的手势体系知识,还是很值得回味的。

*注: 其中 Offset(322.8, 746.9) 是触点的位置,是刚才通过调试看到的 + 位置。

void _pressAdd() {const PointerEvent addPointer =  PointerAddedEvent(pointer: 0,position: Offset(322.8, 746.9));const PointerEvent downPointer =  PointerDownEvent(pointer: 0,position: Offset(322.8, 746.9));const PointerEvent upPointer =  PointerUpEvent(pointer: 0,position: Offset(322.8, 746.9));GestureBinding.instance!.handlePointerEvent(addPointer);GestureBinding.instance!.handlePointerEvent(downPointer);GestureBinding.instance!.handlePointerEvent(upPointer);
}

2. 滑动事件的触发

如下,滑动事件的触发关键点在于 tag1 处,通过 for 循环模拟 20 次偏移量是 20 的向上滑动事件。

void _pressMove() async {const PointerEvent addPointer =  PointerAddedEvent(pointer: 1,position: Offset(122.8, 746.9));const PointerEvent downPointer =  PointerDownEvent(pointer: 1,position: Offset(122.8, 746.9));GestureBinding.instance!.handlePointerEvent(addPointer);GestureBinding.instance!.handlePointerEvent(downPointer);double dy = 20;double updateCount = 20;for (int i = 0; i < 20; i++) { // tag1await Future.delayed(const Duration(milliseconds: 6));PointerEvent movePointer =  PointerMoveEvent(pointer: 1,delta: Offset(0, -dy),position: Offset(122.8, 746.9 - i * dy));GestureBinding.instance!.handlePointerEvent(movePointer);}PointerEvent upPointer = PointerUpEvent(pointer: 1,position: Offset(122.8, 746.9 - dy * updateCount));GestureBinding.instance!.handlePointerEvent(upPointer);
}

这样就可以发现: 只要我们按照各手势检测器竞技胜利的规则进行模拟处理 PointerEvent 事件,就可以通过代码完成我们想要触发的手势,是不是感觉非常棒。感觉可以结合一下计时器通过发送一系列手势来完成一些引导操作,或者操作演示。

对于一些流程性的测试,或精准的滑动控制分析,用代码模拟会显得更加重要,因为一些性能分析需要控制变量,手动滑动多多少少会有不同,从而影响测试分析的结果。本篇就到这里,希望通过本文您能对 Flutter 的手势有更深切的认识,也希望 Flutter 模拟事件触发,在某个时刻可以帮助到您。


长按右侧二维码

查看更多开发者精彩分享

"开发者说·DTalk" 面向中国开发者们征集 Google 移动应用 (apps & games) 相关的产品/技术内容。欢迎大家前来分享您对移动应用的行业洞察或见解、移动开发过程中的心得或新发现、以及应用出海的实战经验总结和相关产品的使用反馈等。我们由衷地希望可以给这些出众的中国开发者们提供更好展现自己、充分发挥自己特长的平台。我们将通过大家的技术内容着重选出优秀案例进行谷歌开发技术专家 (GDE) 的推荐。

 点击屏末 |  | 即刻报名参与 "开发者说·DTalk"


探索 Flutter 模拟事件触发 | 开发者说·DTalk相关推荐

  1. python制作界面怎么触发事件_python模拟事件触发机制详解

    本文实例为大家分享了python模拟事件触发机制的具体代码,供大家参考,具体内容如下 EventManager.py # -*- encoding: UTF-8 -*- # 系统模块 from que ...

  2. python事件触发机制_python模拟事件触发机制详解

    本文实例为大家分享了python模拟事件触发机制的具体代码,供大家参考,具体内容如下 EventManager.py # -*- encoding: UTF-8 -*- # 系统模块 from que ...

  3. JavaScript 模拟事件触发

    事件触发的模拟步骤 Step 1. document.createEvent(eventType) eventType 共5种类型:Events.HTMLEvents.UIEevents.MouseE ...

  4. Flutter 混合开发和组件化实践 | 开发者说·DTalk

    本文原作者: 李伟,原文发布于: 印象笔记 https://app.yinxiang.com/fx/0390f0f2-1770-4bdc-a3c4-d134a6bc654b 引言 在接入 Flutte ...

  5. 字节跳动为什么选用 Flutter : 可能成为不一样的未来 | 开发者说·DTalk

    本文原作者: 袁辉辉,原文发布于微信公众号: Gityuan  https://mp.weixin.qq.com/s/SaIAQ22gbB4nJsDQj3QwrQ 2018 年 12 月,Google ...

  6. Flutter TV 应用的开发尝试 | 开发者说·DTalk

    本文原作者: 谭东,原文发布于公众号谭东 jay: https://mp.weixin.qq.com/s/GI5g-zdeRwc8_E2zPN8pMA 我们之前一直在做 Flutter 在移动端的应用 ...

  7. 已开源!Flutter 流畅度优化组件 Keframe | 开发者说·DTalk

    本文原作者: Nayuta,原文发布于: 进击的 Flutter 列表流畅度优化 这是一个通用的流畅度优化方案,通过分帧渲染优化由构建导致的卡顿,例如页面切换或者复杂列表快速滚动的场景. 代码中 ex ...

  8. Flutter 里的语法糖解析,知其所然方能潇洒舞剑 | 开发者说·DTalk

    本文原作者: 恋猫de小郭,原文发布于: GSYTech 本篇主要针对 Flutter 里 Dart 的一些语法糖实现进行解析,让您明白简单声明的关键字背后,Dart 究竟做了什么? 如下图所示,起因 ...

  9. 不一样角度带您了解 Flutter 中的滑动列表实现 | 开发者说·DTalk

    本文原作者: 恋猫de小郭,原‍文发布于: GSYTech 本篇主要帮助剖析理解 Flutter 里的列表和滑动的组成,用比较通俗易懂的方式,从常见的 ListView 到 NestedScrollV ...

最新文章

  1. firefox html5 canvas,html5 Canvas
  2. matlab用于系统框图建模的函数,MATLAB产品家族中文
  3. 制作精美的网站首页模板应该如何操作?
  4. SphereFace的原理
  5. 利用canvas来绘制一个会动的图画
  6. python:单例模式--使用__new__(cls)实现
  7. HDU——2768 Cat vs. Dog
  8. 鸿蒙眼镜怎么样,Babiators儿童太阳镜怎么样 Babiators儿童太阳镜测评
  9. Web前端开发基础三剑客学习知识分享
  10. android解析JSON数组
  11. ExtJs6 FontAwesome 图标引用
  12. eyoucms相关问题总结
  13. Pr 音频效果参考:降噪/恢复
  14. 使用词向量嵌入模型,获得近义词
  15. NP架构-汇聚层路由器完美选择
  16. bt_迅雷_种子文件后缀名
  17. 11岁的Tumblr:开启艰难禁黄之路
  18. 团队里不能留的三种人
  19. android从九宫格全屏预览,仿微信朋友圈展示图片的九宫格图片展示控件,支持点击图片全屏预览大图...
  20. 轻博客只是大众喜新厌旧的产物

热门文章

  1. java 2分钟_Java怎么判断他是否在2分钟内执行了任务?
  2. 存储过程之八-java代码调用oracle存储过程
  3. VC开发程序创建Dump文件
  4. 在PS中多种类抠图的教程(第一课)后附PS软件可下载
  5. 火箭图标c语言的软件,PS手把手教你绘制酷炫有型的火箭图标
  6. [hdu-1814] Peaceful Commission题解
  7. 用于通过声波捕获显示视觉,触觉和音频的全息显示(A volumetric display for visual, tactile and audio presentation using acous)
  8. 新手玩转Linux Kernel漏洞之Null Pointer Dereference
  9. AlexNet论文翻译与解读
  10. Windos10使用手册