FlutterFlutter原理 HitTestBehavior 介绍

HitTestBehavior 是一个枚举

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,
}
  • deferToChild

deferToChild: 命中测试决定于子组件是否通过命中测试

  • opaque

顾名思义不透明的,也就是说自己接收命中测试,但是会阻碍前兄弟节点进行命中测试

  • translucent

顾名思义半透明,也就是说自己可以命中测试,但是不会阻碍前兄弟节点进行命中测试

demo 测试

import 'package:flutter/material.dart';

class HitTestWidget extends StatefulWidget {
  const HitTestWidget({Key? key}) : super(key: key);

  @override
  State<HitTestWidget> createState() => _HitTestWidgetState();
}

class _HitTestWidgetState extends State<HitTestWidget> {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Align(
          alignment: Alignment.center,
          child: hitTest(1),
        ),
        Align(
          alignment: Alignment.center,
          child: hitTest(2),
        )
      ],
    );
  }
}

Widget hitTest(int index) {
  return Listener(
    behavior: HitTestBehavior.translucent,
    onPointerDown: (e) {
      print("$index");
    },
    child: Container(
      width: index == 1 ? 150 : 50,
      height: index == 1 ? 150 : 50,
      color: index == 1 ? Colors.green : Colors.red,
    ),
  );
}

在这里插入图片描述

  • 当两控件有颜色时

    behavior 为translucent, deferToChild和 opaque 时 两控件的点击都一样,点击红色输出 2, 点击绿色输出1

  • 当去掉两控件的颜色时

    behavior 为deferToChild时,点击两控件并不会输出
    behavior 为opaque时, 点击红色位置的控件输出2, 点击绿色位置的控件输出1
    behavior 为translucent时,点击红色的位置的控件输出1、2, 点击绿色位置的控件输出1

原因

  • 原因查看hittest()方法
    • 当Container 设置颜色时,真正的渲染控件为ColoredBox他的randerObject 为_RenderColoredBox继承了RenderProxyBoxWithHitTestBehavior,所以真正处理behavior是在RenderProxyBoxWithHitTestBehavior中,RenderProxyBoxWithHitTestBehavior的hitTestSelf 方法的返回的是 behavior == HitTestBehavior.opaque; behavior 在_RenderColoredBox 中默认传的值为HitTestBehavior.opaque,所以Container 设置颜色后的behavior都是 HitTestBehavior.opaque,所以点击哪个控件哪个控件命中,输出对应的值
    • 当Container 不设置颜色时,
      • behavior 为deferToChild时 他的randerObject是 ConstrainedBox,而它里面没有对点击事件做判断的地方,同时 Listener 的 RenderProxyBoxWithHitTestBehavior中只是处理了,HitTestBehavior.translucent和HitTestBehavior.opaque,故点击无响应。
      • behavior 为opaque时,会调用 Listener 的 RenderProxyBoxWithHitTestBehavior,而RenderProxyBoxWithHitTestBehavior中的hitTestSelf 方法中返回的是 behavior == HitTestBehavior.opaque;所以 hitTestSelf方法返回true 同时在hitTest()方法中将该控件的区域加入了BoxHitTestResult中 所以命中该控件,该控件会收到点击事件,并将该事件消费掉
      • behavior 为translucent时, 会调用 Listener 的 RenderProxyBoxWithHitTestBehavior,而RenderProxyBoxWithHitTestBehavior中的hitTestSelf 方法中返回的是 behavior == HitTestBehavior.opaque;所以 hitTestSelf方法返回false, 所以该事件没有childview 命中,没有view命中说明不会被消费,但是hitTest() 中 如果behavior == HitTestBehavior.translucent,会将点击的区域BoxHitTestEntry 加入到BoxHitTestResult 中,此时就会触发该点击区域内的所有包含该位置的Widget的事件,所以会同时收到 点击红色区域的输出2和点击绿色区域输出的1
    class _RenderColoredBox extends RenderProxyBoxWithHitTestBehavior {
      _RenderColoredBox({ required Color color })
        : _color = color,
          super(behavior: HitTestBehavior.opaque);

      /// The fill color for this render object.
      ///
      /// This parameter must not be null.
      Color get color => _color;
      Color _color;
      ....

    abstract class RenderProxyBoxWithHitTestBehavior extends RenderProxyBox {
      /// Initializes member variables for subclasses.
      ///
      /// By default, the [behavior] is [HitTestBehavior.deferToChild].
      RenderProxyBoxWithHitTestBehavior({
        this.behavior = HitTestBehavior.deferToChild,
        RenderBox? child,
      }) : super(child);

      /// How to behave during hit testing when deciding how the hit test propagates
      /// to children and whether to consider targets behind this one.
      ///
      /// Defaults to [HitTestBehavior.deferToChild].
      ///
      /// See [HitTestBehavior] for the allowed values and their meanings.
      HitTestBehavior behavior;

      @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;
      ........
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值