首页 > 其他分享 >flutter 基础 —— 事件监听

flutter 基础 —— 事件监听

时间:2023-01-10 18:25:04浏览次数:39  
标签:节点 child 监听 hitTest result onTap position flutter 事件

事件机制:

  1. 命中测试的过程是从上层组件到下层组件,但是加入 HitTestResult 的顺序是从下到上,分发事件的顺序同加入顺序。
  2. 通常,若用户点击坐标不在当前节点的 size 范围内,则 hitTest 直接返回 false。
  3. 命中测试过程中,① 兄弟节点的加入顺序是倒序的,这个可以结合 Stack 组件来理解,即上层组件(children 中靠后)优先遍历。② 一旦有一个子节点的 hitTest 返回了 true,就会终止遍历。因为通常兄弟节点通常是不重叠的,且用户点击的坐标位置只会有一个节点,所以只要一个子节点命中了 hitTest 就返回 true,并且终止遍历。
  4. 节点自身是否通过命中测试的标志是它被添加到 HitTestResult 列表中,而不是它 hitTest 的返回值。但是 hitTest 的返回值会影响父组件是否被添加到 HitTestResult 中。

示例:

① RenderBox

bool hitTest(BoxHitTestResult result, { required Offset position }) {
  if (_size!.contains(position)) {
    if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
      //添加到命中测试列表
      result.add(BoxHitTestEntry(this, position));
      return true;
    }
  }
  return false;
}

// 通常会该重写该方法
@protected
bool hitTestSelf(Offset position) => false;

② RenderProxyBoxWithHitTestBehavior

// ColoredBox(初始化 behavior 是 HitTestBehavior.opaque)、
// Listener(初始化 behavior 是 HitTestBehavior.deferToChild) 均继承自该组件

@override
bool hitTest(BoxHitTestResult result, { required Offset position }) {
  bool hitTarget = false;
  if (size.contains(position)) {
    hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);
    if (hitTarget || behavior == HitTestBehavior.translucent) {
      result.add(BoxHitTestEntry(this, position));
    }
  }
  return hitTarget;
}

@override
bool hitTestSelf(Offset position) => behavior == HitTestBehavior.opaque;

③ IgnorePointer

@override
bool hitTest(BoxHitTestResult result, { required Offset position }) {
// 未调用 result.add,则当前节点没有命中测试;
// hitTest 返回 false(ignoring 默认为true),表示当前节点不参与测试,
// 如果有其它兄弟节点,则会继续测试下一个兄弟节点。
  return !ignoring && super.hitTest(result, position: position);
}

④ AbsorbPointer

@override
bool hitTest(BoxHitTestResult result, { required Offset position }) {
// 未调用 result.add,则当前节点没有命中测试;
// hitTest 返回 true(absorbing 默认为true),表示前节点已参与测试,
// 如果有其它兄弟节点,则不会继续测试下一个兄弟节点。
  return absorbing
      ? size.contains(position)
      : super.hitTest(result, position: position);
}

⑤ 自定义事件透传组件

代码:

class PassPointer extends SingleChildRenderObjectWidget {
  const PassPointer({this.onTap, super.child});

  final VoidCallback? onTap;

  @override
  RenderObject createRenderObject(BuildContext context) {
    return RenderPassPointer(onTap: onTap);
  }
}

class RenderPassPointer extends RenderProxyBox {
  RenderPassPointer({
    this.onTap,
    RenderBox? child,
  }) : super(child);

  final VoidCallback? onTap;

  @override
  bool hitTest(BoxHitTestResult result, {required Offset position}) {
    if (size.contains(position)) {
      // 将当前节点添加到 HitTestResult 表中,即通过命中测试
      result.add(BoxHitTestEntry(this, position));
      // 返回 false,表示当前节点不参与测试,则会继续测试下一个兄弟节点
      return false;
    } else {
      return super.hitTest(result, position: position);
    }
  }

  @override
  void handleEvent(PointerEvent event, HitTestEntry entry) {
    if (event is PointerDownEvent) {
      onTap?.call();
    }
  }
}

// 展示效果
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Stack(
            alignment: Alignment.center,
            // 多个兄弟节点,事件的遍历从下到上
            children: [
              Container(
                width: 150,
                height: 150,
                color: Colors.yellow,
                child: InkWell(
                  onTap: () {
                    print('C');
                  },
                ),
              ),
              Listener(
                behavior: HitTestBehavior.translucent,
                onPointerDown: (e) => print('bbb'),
                child: IgnorePointer(
                  child: Container(
                    width: 100,
                    height: 100,
                    color: Colors.green,
                    child: InkWell(
                      onTap: () {
                        print('B');
                      },
                    ),
                  ),
                ),
              ),
              PassPointer(
                onTap: () => print("aaa"),
                child: Container(
                  width: 60,
                  height: 60,
                  color: Colors.pink,
                  child: InkWell(
                    onTap: () {
                      print('A');
                    },
                  ),
                ),
              ),
            ],
          )),
    );
  }
}

标签:节点,child,监听,hitTest,result,onTap,position,flutter,事件
From: https://www.cnblogs.com/lemos/p/17041045.html

相关文章

  • vue 父组件给子组件传值监听
    <template><divclass="pie-pic"ref="piePic"></div></template><script>exportdefault{name:"PiePic",props:['piePicOption'],data(){retur......
  • 事件处理_1事件的基本使用
    事件处理_1事件的基本使用<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>事件的基本使用</title><scriptsrc="../js/vue.js"></......
  • 事件处理_3键盘事件
    事件处理_3键盘事件<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title></title><scriptsrc="../js/vue.js"></script></head><bod......
  • 事件处理_2事件修饰符
    事件处理_2事件修饰符<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>事件修饰符</title><scriptsrc="../js/vue.js"></script>......
  • 7. 等待一个事件
    等待一个事件在多线程开发中,当一个线程的运行条件是另外一个线程的运算结果的时间,等待线程通常有几种处理方法1.循环查询,知道满足条件为止2.休眠一个固定的时间,然后查......
  • Unity+Pico 响应射线事件
    1、添加组件为了让场景内的物体能够响应射线的操作,需要在该物体上添加“XRSimpleInteractable”组件,并对射线的交互事件编写脚本看,最常用的是“Hover”和“Select”事件......
  • Flutter异常监控 - 肆 | Rollbar源码赏析
    一.Rollbar可以帮你解决哪些问题无特别说明,文中Rollbar统指Rollbar-flutter1.代码复用Rollbar官方文档说是纯Dart实现,该特征意味着自带”代码复用”光环。如图当接......
  • elementui表格中实现点击单个单元格和表头--带参数触发事件/跳转路由
    对于element表格做点击跳转的功能有两大类:1,表头的点击跳转2,表格内容单元格进行点击跳转是因为该表格只有tabs标签也第二个选项被选中的时候才能让他起效果,所以先做判断,第......
  • shell端口监听异常邮箱告警
    业务场景:应用发布监听服务是否正常启动,因为服务器资源不够上不了prometheus、grafana,所以写的shell脚本监听。此脚本适用于初创公司及小微企业使用。准备工作除了she......
  • jQuery核心对象(伪数组,什么时候可以不写绑定文档加载完成的监听$(function(){},each中又
    伪数组相关文档主要是讲了给1.$()【函数】和$.xxx【方法】2.$xxx.yyy()【$xxx是一种常见的给jQuery对象的命名方式】【给对象用的方法】用的函数和方法。绝大部分都......