我正在参与「启航方案」
点击事情呼应
点击组件中的HitTestBehavior
特点支撑三个值:opaque
、translucent
、deferToChild
。其在射中测验起到必定效果可改动原有射中逻辑从而是完成不同点击触发事情。
HitTestBehavior特点值
HitTestBehavior
在RenderProxyBoxWithHitTestBehavior
有详细完成运用场景。
/flutter/packages/flutter/lib/src/rendering/proxy_box.dart:RenderProxyBoxWithHitTestBehavior
@override
boolhitTest(BoxHitTestResult result, {requiredOffset position }) {
boolhitTarget =false;
if(size.contains(position)) {
hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);// 若behavior 等于HitTestBehavior.opaque可射中
if(hitTarget || behavior == HitTestBehavior.translucent) {// 若behavior等于HitTestBehavior.translucent可射中
result.add(BoxHitTestEntry(this, position));
}
}
returnhitTarget;
}
@override
boolhitTestSelf(Offset position) => behavior == HitTestBehavior.opaque;
在检查子节点射中情况时判别是否opaque
,在检查本身射中时判别translucent
,因而可知HitTestBehavior.opaque
>HitTestBehavior.translucent
>HitTestBehavior.deferToChild
。
因为基础组件关于hitTest
和hitTestChildren
非false
因而存在将事情消费情况(例如SizedBox
无子节点情况下默许false
)。为了验证HitTestBehavior
可行性就需求自定义一个组件重写hitTest
和hitTestChildren
方法。这里以ColoredBox
为基础自定义组件重写_RenderColoredBox
的射中判定默许都为false
。
@override
boolhitTest(BoxHitTestResult result, {requiredOffset position}) {
returnfalse;
}
@override
boolhitTestChildren(BoxHitTestResult result, {requiredOffset position}) {
returnfalse;
}
在Stack
中设置两个堆叠子节点:
- 默许情况下点击组件
behavior
是HitTestBehavior.deferToChild
根据点击组件子节点射中测验要求而定 - 当为
HitTestBehavior.deferToChild
情况下,点击事情由最上层子节点呼应 - 当为
HitTestBehavior.translucent
情况下,点击事情都透传一切子节点都呼应 - 当为
HitTestBehavior.deferToChild
情况下,点击事情无呼应(因为自定义_ColoredBoxWithNoHitTest疏忽了射中测验)
Stack(
alignment: Alignment.center,
children: [
Listener(
behavior: value,
onPointerDown: (down) {
showSnackBarMsg(context,'onPointerDown -> Listener -> 外',
clear:false, duration:constDuration(milliseconds:500));
},
child: _ColoredBoxWithNoHitTest(
color: Colors.red.withOpacity(0.5),
child:constSizedBox(
height:200,
width:250,
),
)),
Listener(
behavior: value,
onPointerDown: (down) {
showSnackBarMsg(context,'onPointerDown -> Listener -> 内',
clear:false, duration:constDuration(milliseconds:500));
},
child: _ColoredBoxWithNoHitTest(
color: Colors.red.withOpacity(0.5),
child:constSizedBox(
height:100,
width:150,
),
),
),
],
);
在运用
HitTestBehavior
也需求注意到所效果节点是否支撑疏忽射中,因为很多情况下有些组件默许情况下完成hitTest
是true
状况。
实战举例
一个布局完成如下:最外层Container
设置边框可视化点击区域,内部子节点是带有GestureDetector
的Container
无边框无背景色其内部子节点有Image
、Text
等。
Container(
decoration: buildBoxDecorationBorder(),
child: GestureDetector(
// behavior: HitTestBehavior.deferToChild, // 设置后点击空白区域无呼应
behavior: HitTestBehavior.translucent,// 设置后点击空白区域有呼应
onTap: () {
showSnackBarMsg(context,'onTap -> GestureDetector -> Container');
},
child: Container(
alignment: Alignment.center,
height:150,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/img_640_640.jpg', width:50),
constText(
"文本区域\n"
"文本区域\n"
"文本区域\n"
"文本区域\n",
style:
TextStyle(color: Colors.white, backgroundColor: Colors.red),
),
ColoredBox(
color: Colors.red.withOpacity(0.5),
child:constSizedBox(
width:150,
height:100,
child: Center(
child: Text(
"behavior是opaque或translucent\n点击空白区域才干呼应",
style: TextStyle(color: Colors.white),
),
),
),
)
],
),
),
),
);
- 默许情况下点击子节点
Container
内部子组件能够呼应点击事情 - 点击子节点
Container
内部空白区域无法呼应点击事情 - 修改
GestureDetector
的behavior
为HitTestBehavior.translucent
点击边框内任务区域都能呼应点击事情
剖析缘由
Container
是复合组件由多种其他组件嵌套而成,例如装备color
会嵌套ColoredBox
,增加边框decoration
会嵌套DecoratedBox
。
ColoredBox特点
ColoredBox
内部完成_RenderColoredBox
,它的射中测验逻辑是由。RenderProxyBoxWithHitTestBehavior
判别
/flutter/packages/flutter/lib/src/rendering/proxy_box.dart:RenderProxyBoxWithHitTestBehavior
@override
boolhitTest(BoxHitTestResult result, {requiredOffset position }) {
boolhitTarget =false;
if(size.contains(position)) {
hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);
if(hitTarget || behavior == HitTestBehavior.translucent) {
result.add(BoxHitTestEntry(this, position));
}
}
returnhitTarget;
}
@override
boolhitTestSelf(Offset position) => behavior == HitTestBehavior.opaque;
是否射中测验以hitTestChildren
作为判别根据,因为默许情况下behavior
是deferToChild
。一般情况而言子节点射中测验都是true
,所以有Color
的Container
是一般是射中测验的。
非也非也阅览_RenderColoredBox
源码可知默许情况下behavior
是HitTestBehavior.opaque
状况,因而触摸组件监听时能够直接通过射中测验。
/flutter/packages/flutter/lib/src/widgets/basic.dart:_RenderColoredBox
class_RenderColoredBoxextendsRenderProxyBoxWithHitTestBehavior{
_RenderColoredBox({requiredColor color })
: _color = color,
super(behavior: HitTestBehavior.opaque);
...
}
DecoratedBox特点
DecoratedBox
内部完成了RenderDecoratedBox
其hitTestSelf
方法完成由BoxDecoration
接收,从内部完成看射中测验逻辑可知
/flutter/packages/flutter/lib/src/painting/box_decoration.dart:BoxDecoration
@override
boolhitTest(Size size, Offset position, { TextDirection? textDirection }) {
...
switch(shape) {
caseBoxShape.rectangle:// 矩形求边框是否在范围内
if(borderRadius !=null) {
finalRRect bounds = borderRadius!.resolve(textDirection).toRRect(Offset.zero & size);
returnbounds.contains(position);
}
returntrue;
caseBoxShape.circle:// 圆形求半径是否在射中范围内
finalOffset center = size.center(Offset.zero);
finaldoubledistance = (position - center).distance;
returndistance <= math.min(size.width, size.height) /2.0;
}
}
因而设置了边框的Container
只要在边框范围内射中测验都是true
小总结
如上所知当Container
没有设置Color
和DecoratedBox
特点时若要让Container
整体射中测验就必须为点击组件设置为translucent
或者opaque
,相反则只能呼应Container
内部子节点事情。