隔离的这14天,慢慢的研究了Flutter的指针事件,在这个过程中,又重新梳理了一下Element和Render Tree的形成过程。这篇文章,主要对指针事件在Fluter中如何下发到各个组件的过程进行梳理。(指针是指针,手势是手势,手势是指针事件的某种行为,且只有一个胜者,这点要区分清楚。)
好像要一只dash啊。
好的,进入正题。当你点击了屏幕,Flutter做了什么呢?
通过调用栈,我们逐步分析过程。
指针数据被包装成Dart的ByteData
类型,调用PlatformDispatcher
的_dispatchPointerDataPacket
方法,至于指针数据怎么被平台包装,内容是什么,这就涉及到各个平台处理指针的方式,掘金由许多相关文章。(作者目前不懂任何原生知识)
经过了几层看不懂的调用,指针数据走到了这个地方,并且以队列
的形式被处理。(这里又有window,又有lock,暂时不管)。
注意到,这里的packet是ui.PointerDataPacket
类型,该类内部仅有一个final List<PointerData>
的data成员。
既然是队列
,那么就是循环出列,逐个处理事件。这个过程由_flushPointerEventQueue
完成。
handlePointerEvent
方法,如其名,这里传过来的数据已经成为了PointerEvent
类型。(resanlingEnabled
默认为false,官方文档的解释是通过这只这个选项,对于设备的指针采样率
和屏幕刷新率
有某种关系的,可以让指针更丝滑)
_handlePointerEventImmediately
方法被handlePointerEvent
(上面那个家伙)调用。 这个方法是个重量级的家伙。
void _handlePointerEventImmediately(PointerEvent event) {
HitTestResult? hitTestResult;
if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent) {
//省略...
hitTestResult = HitTestResult();
hitTest(hitTestResult, event.position); //看这里!!!!!!!!!!!
//省略...
}
dispatchEvent(event, hitTestResult);
}
}
复制代码
到这里,先缓一缓,也许上面我写的不好看不懂也没关系,你只需要记住一件事,我们已经拿到了类型为PointerEvent
的指针事件的数据event
。
(PointerEvent
有多个子类,PointerDownEvent
,PointerMoveEvent
,PointerUpEvent
等等,对应点击
,移动
,抬起
)
这里以PointerDownEvent
举例,这个事件为用户点击屏幕后产生的。
为什么用户要点击?我猜他在某种APP内发现了一张涩图
想点进去看看。图片肯定是个RenderObject
(不然你能看到个**),那写代码的怎么知道用户点的是哪张图
呢?
Flutter带Hit
开头的接口帮我们做这件事,和RenderObject
相关的有三个接口。
1. HitTestable
的hitTest
方法,让这个RenderObject
能点,什么是能点?稍后的HitTestResult
就会告诉你。
2. HitTestDispatcher
的dispatchEvent
方法,嗯,能发事件。
3. HitTestTarget
的handleEvent
方法,RenderObject
能被点了,那事件你处理不处理,怎么处理,就是这个方法的内容了。
从RenderView
的hitTest
进行递归,跟据点击指针事件event
的position
,调用child
的hitTest
。RenderView
是RenderTree的根,怎么来的可以看看Binding
的相关内容。
这里插一则,GestureBinding
有hitTest
,RendererBinding
也有,从runApp
方法可以看到调用内容,super.hitTest
对应GestureBinding
的hitTest
,截图对应的是RendererBinding
。(不影响阅读后面的内容)
RenderTree从RenderView
开始hitTest
。大多数情况下,我们创建的都是RenderBox
,这个盒模型,有长和宽等大小信息,使用笛卡尔坐标系。有的有单个child
,或者双链表式的children
。面对这多种情形,不同的组件有不同的点击测试
内容。
这里通过Stack
和Container
组件,让大家理解下这个递归过程。
Stack(
children: [
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: (){print("blue");},
child: Container(width: 300,height: 300,color:Colors.blue)),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: (){print("red");},
child: Container(width: 150,height: 150,color:Colors.red))
],
),
复制代码
某些乱七八糟的BLOG说设置GestureDetector
的behavior
就能实现点击穿透
,然而点击红色方块控制台只输出red。
这是为什么呢?答案是和hitTest
有关。
Stack
对应的是RenderStack
,是一个双链表
的child
模型(由ContainerRenderObjectMixin
实现)。其hitTest
是RenderBox
的方法,原汁原味。
如果这个Box包含点击点,通过hitTestChildren
先对children
逐个进行hitTest
,前者不中则再通过hitTestSelf
自己进行hitTest
。只要中了,就把自己添加进result
。
result
储存的是所有通过的测试的RenderBox
,这些都会接收到指针事件。
RenderStack
的hitTestChildren
hitTest
过程可以解释为从lastChild
开始,向前进行点击测试,直到有一个child
通过,addWithPaintOffset
的内部会添加进result
,然后返回true
。
为什么从lastChild
开始?因为是栈顶
对应的RenderBox
,这样就保证了上面的盖住了下方的,使得一般情况下的点击无法穿透。
hitTestSelf
默认返回false
,子类可以根据需要重写。(GestureDetector
,RenderPointerListener
,Listener
,RenderBoxWithHitTestBehavior
等有详细的内容,之后的点击穿透会讲)
所以结论是,在HitTestChildren
中,红色方块的RenderBox
被添加进了HitTestResult
中,此时就跳出循环,递归回调,所以蓝色方框得不到指针信息。
hitTest
总结这是一个自上而下
,递归
的过程,内部主要由hitTestChildren
和hitTestSelf
实现。点击处的坐标在RenderBox
的内部是能够进行hitTest
的前提,但是通不通过hitTest
取决于组件内部自己是如何实现hitTest
,能否接收到指针事件取决于是否把RenderBox
添加到HitTestResult
中。
好奇result
的内容,在_handPointEventImmediately
中打印result
即可。
把获得的HitTestResult
通过内部path
遍历,调用各个RenderObject
的handleEvent
。其实内部还有pointRoute
等内容,暂时没研究。
在插播一条无关消息,GestureBinding
这个方法下面的handleEvent
就是手势竞技场的内容。(手势是手势的事情,指针不管手势的事)
所以平台的指针事件下发需经历如下3个过程。
包装成Flutter能看得懂的指针数据
挑选出需要响应事件的RenderObject
分发事件执行RenderObject
的handleEvent
更多Android知识,扫码即可了解
<img src="https://hnxx.oss-cn-shanghai.aliyuncs.com/official/1704935888404.jpg?t=0.014764499839815759" style="margin: auto" />
文章浏览阅读1w次,点赞7次,收藏11次。最近想跑一些主流的网络感受感受。从github上找到了 deep-learning-models 提供的几个模型,包括:inception-v2, inception-v3, resnet50, vgg16, vgg19 等等。这些代码都是基于 keras 框架,正好我最近有在学 tensorflow 和 keras,所以很想跑跑这些代码。心动不如行动,准备工作都做得差不多了,准备开始跑代码。此时,出现了一些常见的问题,也正好借此机会整理下来。_shape为什么报错
文章浏览阅读1.5k次。参考1:http://blog.csdn.net/areswhy/article/details/8697527参考2:http://www.cnblogs.com/emouse/archive/2013/01/29/2881721.html(windows下Qt安装部分)前记:一两个月以后就要找工作了,本来想学学embedded-linux Qt,无奈电脑老旧,用的虚拟机装的ubuntu_xp的系统装qt
文章浏览阅读2.2k次。Iris数据集概况Iris Data Set(鸢尾属植物数据集)是我现在接触到的历史最悠久的数据集,它首次出现在著名的英国统计学家和生物学家Ronald Fisher 1936年的论文《The use of multiple measurements in taxonomic problems》中,被用来介绍线性判别式分析。在这个数据集中,包括了三类不同的鸢尾属植物:Iris Setosa,I..._iris_data.mat
文章浏览阅读1.1w次,点赞4次,收藏3次。React在安装antd之后出现的Can’t resolve './locale’或者浏览器显示Cannot find module './locale’问题,是因为moment的版本有问题,而react默认使用了最新的moment,但是在[email protected]中是没有问题的。1.第一种解决方法解决方案就是配置webpack的alias,将所有的 moment 路径引用导入到 [email protected]操作步骤安装moment 依赖 npm install [email protected]_react脚手架 can't resolve './const
文章浏览阅读161次。其实Redis并不正式支持windows版本,官网那个5.0.x稳定版其实是linux版本_(二)redis下载和安装
文章浏览阅读4.3w次,点赞10次,收藏23次。这个网上给我的提示(http://zhidao.baidu.com/link?url=UNxy0GoaU7jj0QRgCikKIdHIrE7C-FOiojG-5nE6a_QBqXVuYdublOROizQyNRtfNudH53WZQrGj6Dgv_sXcr_):默认值设置:Tools_Preference_PCB Editor_Defaults,选Component点Edit Values.._ad10统一修改字符大小
文章浏览阅读7.4k次。zencart1.55内嵌支付获取卡号_zencart 回调方法checkout_process 提交参数
文章浏览阅读474次。多表连接一.什么是约束二,注意三,表级约束和列级约束命令行:四,定义约束五,unique约束命令语句:六,主键约束命令语句01:命令语句02:七,foreign key 约束命令语句:七,foreign key 约束的关键字八,check 约束命令行:..._plsql里面约束在哪显示
文章浏览阅读1w次,点赞2次,收藏2次。javax.net.ssl.SSLException: Received fatal alert: internal_error_javax.net.ssl.sslexception: received fatal alert: internal_error
文章浏览阅读1.3k次,点赞2次,收藏2次。1、拼接处truncate所有表的语句select 'truncate table '||table_name||';' from user_tables;2、Ctrl+A全选,粘贴至命令行,执行Over._神通数据库清除数据的脚本
文章浏览阅读1w次。苹果IOS程序开发不同分辨率的设备统一为一个尺寸而标记的。@3X就是@1X分辨率的3倍。如图,iPad2 是768 x 1024,iPad Retina 是1536 x 2048,开发时都按 768 * 1024 操作。但实际上两者有一倍差异。为了达到最佳效果,使用的图片大小不一样。这时候就用同一个名称,但 Retina 的图加上 @2x 后缀。系统加载图片时,在 iPad2 上会加载 @_web1x 2x
文章浏览阅读1.7w次,点赞4次,收藏8次。一、之前写的实现直接js判断就可以.prevent .stop 是阻止继续冒泡 不一样的见 官方说明 1https://cn.vuejs.org/v2/cookbook/form-validation.html#%E4%BD%BF%E7%94%A8%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%A1%E9%AA%8C2 自定义 (我没看)https://..._vue2对el-select和el-input如何判断是否为空