/// How to behave during hit tests.
enum HitTestBehavior {
/// Targets that defer to their children receive events within their bounds
/// only if one of their children is hit by the hit test.
deferToChild,
/// Opaque targets can be hit by hit tests, causing them to both receive
/// events within their bounds and prevent targets visually behind them from
/// also receiving events.
opaque,
/// Translucent targets both receive events within their bounds and permit
/// targets visually behind them to also receive events.
translucent,
}
HitTestBehavior.deferToChild
是默认的配置
例子
当给 Container
设置颜色的时候,点击有响应。而去掉颜色,则点击无响应。
GestureDetector(
onTap: () {
print("onTap");
},
child: Container(
color: Colors.blue,
width: 40,
height: 40,
),
),
在 Container
的源码里可以看到,如果 color != null
,那么会给一个 ColoredBox
。
// Container.dart
if (color != null)
current = ColoredBox(color: color!, child: current);
而 ColoredBox
里的 createRenderObject
是 _RenderColoredBox
所创建的:
// ColoredBox
@override
_RenderColoredBox createRenderObject(BuildContext context) {
return _RenderColoredBox(color: color);
}
由于 RenderColoredBox
继承自 RenderProxyBoxWithHitTestBehavior
, 在 RenderColoredBox
构造方法里,会默认传入 HitTestBehavior.opaque
,使其可以让自己命中点击。
class _RenderColoredBox extends RenderProxyBoxWithHitTestBehavior {
_RenderColoredBox({ required Color color })
: _color = color,
super(behavior: HitTestBehavior.opaque);
...
}
abstract class RenderProxyBoxWithHitTestBehavior extends RenderProxyBox {
RenderProxyBoxWithHitTestBehavior({
this.behavior = HitTestBehavior.deferToChild,
RenderBox? child,
}) : super(child);
/// How to behave during hit testing.
HitTestBehavior behavior;
...
@override
bool hitTestSelf(Offset position) => behavior == HitTestBehavior.opaque;
...
}
而如果没有给颜色的话,会给的是 ConstrainedBox
,而它里面没有对点击事件做判断的地方,故点击无响应。
if (constraints != null)
current = ConstrainedBox(constraints: constraints!, child: current);
因此我们针对这种情况就直接对GestureDetector
的behavior赋予HitTestBehavior.opaque
,就能使其点击事件有响应,因为GestureDetector
的源码一路跟下去,它的实现里也有RenderProxyBoxWithHitTestBehavior
。
如果我们给Container
去掉color
属性,赋予一个空的BoxDecoration
:
GestureDetector(
onTap: () {
print("onTap");
},
child: Container(
decoration:BoxDecoration(),
width: 40,
height: 40,
),
),
它是可以响应点击事件的,因为会给一个DecoratedBox
:
if (decoration != null)
current = DecoratedBox(decoration: decoration!, child: current);
DecoratedBox
的内部实现里有 RenderDecoratedBox
,这里有对hitTest
的处理:
class RenderDecoratedBox extends RenderProxyBox {
...
@override
bool hitTestSelf(Offset position) {
return _decoration.hitTest(size, position, textDirection: configuration.textDirection);
}
...
}