[Flutter] Listener触摸手势监听
Listener组件
对触摸,手势的监听。
本文是对下面链接文章的笔记总结:
详情请看:深入理解Flutter的Listener组件
Listener组件属性
const Listener({
Key key,
this.onPointerDown,
this.onPointerMove,
this.onPointerEnter,
this.onPointerExit,
this.onPointerHover,
this.onPointerUp,
this.onPointerCancel,
this.onPointerSignal,
this.behavior = HitTestBehavior.deferToChild,
Widget child,
})
命中测试(Hit Test)
当手指按下、移动或者抬起时,Flutter
会给每一个事件新建一个对象,如按下是PointerDownEvent
,移动是PointerMoveEvent
,抬起是PointerUpEvent
。对于每一个事件对象,Flutter
都会执行命中测试。
是否命中取决于hitTestChildren方法和hitTestSelf方法。
hitTestChildren:如果自己的子widget命中,那父widget也命中测试。
hitTestSelf:如果自己命中测试,加入测试列表,返回true
命中测试顺序:每一个组件都会生成一个RenderObject对象,从最低层的对象开始判断是否命中测试。
如:
Widget和它的RenderObject对象(后面的注释,每个组件一个
Listener(//RenderPointerListener
child: ConstrainedBox(//RenderConstrainedBox
constraints: BoxConstraints.tight(Size(200, 200)),
child: Center(//RenderPositionedBox
child: Text('click me'),//RenderParagraph
)
),
onPointerDown: (event) => print("onPointerDown")
)
命中测试列表
//因为RenderParagraph命中,所以最后RenderPointerListener也命中,执行handleEvent方法,打印onPointerDown
RenderParagraph -> RenderPositionedBox -> RenderConstrainedBox -> RenderPointerListener
行为Behavior
HitTestBehavior.deferToChild //Listener是否命中测试,取决于子child是否命中测试,这是默认behavior的默认值。
HitTestBehavior.opaque //当Listener的子child没有命中测试时,该属性值保证hitTestSelf返回true,即保证Listener所在区域能响应触摸事件。
HitTestBehavior.translucent //当Listener的子child没有命中测试时,并且hitTestSelf返回false时,该属性值可以保证Listener所在的区域能响应触摸事件 (加入到命中测试列表),但是hitTest方法返回值还是false,这不能改变。
Behavior测试
Stack(
children: <Widget>[
// listener 1
Listener(
child: ConstrainedBox(
constraints: BoxConstraints.tight(Size(400, 200)),
child: Container(
color: Colors.blue,//设置了颜色
)
),
onPointerDown: (event) => print("onPointerDown1"),
),
// listener 2
Listener(
child: ConstrainedBox(
constraints: BoxConstraints.tight(Size(400, 200)),
child: Center(child: Text("dont click me")),
),
onPointerDown: (event) => print("onPointerDown2"),
// behavior: HitTestBehavior.opaque, //注释1
// behavior: HitTestBehavior.translucent, //注释2
)
],
),
情况1:
behavior:HitTestBehavior.deferToChild //默认
//不点击Text
RenderStack的hitTestChildren先找Stack的最上层child(listener2),listener2因为没点击Text区域,所以没有通过命中测试,hitTestSelf为false
RenderStack的hitTestChildren又找到listener1,listener1的子组件container因为被颜色覆盖,点击通过命中测试,所以listener1通过命中测试,
结果:打印onPointerDown1
情况2:
behavior:HitTestBehavior.opaque //默认
//不点击Text
RenderStack的hitTestChildren先找Stack的最上层child(listener2),因为listener2的behavior为HitTestBehavior.opaque,即使没点击text也会hitTestSelf也会返回true,即通过命中测试。而这时候,RenderStack的hitTestChildren直接返回了true,它并不会再去检测第二个child。
结果:打印onPointerDown2
情况3:
behavior:HitTestBehavior.translucent //默认
//不点击Text
RenderStack的hitTestChildren先找Stack的最上层child(listener2),因为listener2的behavior为HitTestBehavior.translucent,listener2命中测试,但是hitTestSelf返回的是false,然后RenderStack会再去找第二个child即listener1,listener1毫无疑问通过命中测试。
结果:打印
onPointerDown2
onPointerDown1