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`的相关内容。
![image.png](https://img-blog.csdnimg.cn/img_convert/2d9be33905ea76a50db68ea35e8e3535.webp?x-oss-process=image/format,png)
这里插一则,`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))
],
),
复制代码
![image.png](https://img-blog.csdnimg.cn/img_convert/b5831e79dd7ea3fae57eff0b55e94bc1.webp?x-oss-process=image/format,png)
某些乱七八糟的BLOG说设置`GestureDetector`的`behavior`就能实现`点击穿透`,然而点击红色方块控制台只输出red。
这是为什么呢?答案是和`hitTest`有关。
`Stack`对应的是`RenderStack`,是一个`双链表`的`child`模型(由`ContainerRenderObjectMixin`实现)。其`hitTest`是`RenderBox`的方法,原汁原味。
如果这个Box包含点击点,通过`hitTestChildren`先对`children`逐个进行`hitTest`,前者不中则再通过`hitTestSelf`自己进行`hitTest`。只要中了,就把自己添加进`result`。
`result`储存的是所有通过的测试的`RenderBox`,这些都会接收到指针事件。
![image.png](https://img-blog.csdnimg.cn/img_convert/a2c1d87295d76a4ca1c2b0e6325ed454.webp?x-oss-process=image/format,png)
#### `RenderStack`的`hitTestChildren`
`hitTest`过程可以解释为从`lastChild`开始,向前进行点击测试,直到有一个`child`通过,`addWithPaintOffset`的内部会添加进`result`,然后返回`true`。
为什么从`lastChild`开始?因为是`栈顶`对应的`RenderBox`,这样就保证了上面的盖住了下方的,使得一般情况下的点击无法穿透。
![image.png](https://img-blog.csdnimg.cn/img_convert/f4e116be5f11965de7261604deb9743d.webp?x-oss-process=image/format,png)
![image.png](https://img-blog.csdnimg.cn/img_convert/285bc1de1bc462f3bb362f3251cbb1ac.webp?x-oss-process=image/format,png)
`hitTestSelf`默认返回`false`,子类可以根据需要重写。(`GestureDetector`,`RenderPointerListener`,`Listener`,`RenderBoxWithHitTestBehavior`等有详细的内容,之后的点击穿透会讲)
所以结论是,在`HitTestChildren`中,红色方块的`RenderBox`被添加进了`HitTestResult`中,此时就跳出循环,递归回调,所以蓝色方框得不到指针信息。
### `hitTest`总结
这是一个`自上而下`,`递归`的过程,内部主要由`hitTestChildren`和`hitTestSelf`实现。点击处的坐标在`RenderBox`的内部是能够进行`hitTest`的前提,但是通不通过`hitTest`取决于组件内部自己是如何实现`hitTest`,能否接收到指针事件取决于是否把`RenderBox`添加到`HitTestResult`中。
好奇`result`的内容,在`_handPointEventImmediately`中打印`result`即可。
#### dispatchEvent分发通知
把获得的`HitTestResult`通过内部`path`遍历,调用各个`RenderObject`的`handleEvent`。其实内部还有`pointRoute`等内容,暂时没研究。
![image.png](https://img-blog.csdnimg.cn/img_convert/20de9af295f22a6c65470c3f3bd9ea40.webp?x-oss-process=image/format,png)
在插播一条无关消息,`GestureBinding`这个方法下面的`handleEvent`就是手势竞技场的内容。(手势是手势的事情,指针不管手势的事)
## 总结
所以平台的指针事件下发需经历如下3个过程。
1. 包装成Flutter能看得懂的指针数据
2. 挑选出需要响应事件的`RenderObject`
3. 分发事件执行`RenderObject`的`handleEvent`
更多Android知识,扫码即可了解
<img src="https://hnxx.oss-cn-shanghai.aliyuncs.com/official/1704935888404.jpg?t=0.014764499839815759" style="margin: auto" />
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
**因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/07faa508958f1cdc00f2920873f01d57.png)
![img](https://img-blog.csdnimg.cn/img_convert/7326c4c01d36afd73a98feb011820fde.png)
![img](https://img-blog.csdnimg.cn/img_convert/140aefee59372e4c9f82bb0ff81d7d48.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!**
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新**
**如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)**
![img](https://img-blog.csdnimg.cn/img_convert/5cae5e56bde86cd895c0430ac8b9b8f2.png)
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
帮助,可以添加VX:vip204888 (备注鸿蒙获取)**
[外链图片转存中...(img-RxozD9am-1712834802836)]
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**