Flutter 指针事件原理&点击穿透_flutter pointer

本文探讨了Flutter中指针事件的处理,包括PointerDownEvent、PointerSignalEvent和PointerHoverEvent。通过HitTestResult进行碰撞检测,并展示了如何在Stack组件中使用GestureDetector避免点击穿透问题,详细解释了HitTestBehavior的用法。
摘要由CSDN通过智能技术生成

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行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值