[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
posted @ 2022-04-25 15:21  漫游者杰特  阅读(631)  评论(0编辑  收藏  举报