Flutter 双指缩放和双指移动共存手势检测系列之--2封装

前言

在上一篇《双指缩放和双指移动共存手势系列–1方案》中通过直接在GestureDetector 中加入手势判断代码来实现是双指缩放或是双指触屏移动控件。上一篇也提到个问题,就是该代码与功能代码高度耦合,不利于是多项目之间共享。
本篇讲把该部分代码进行封装,作为一个 package 独立存在。

一、封装效果

在这里插入图片描述
如上图所示,左侧是封装后的控件,右侧是GestureDetector 控件。左侧控件在使用上保持了 GestureDetector 的一致性。新控件GestureDetectorTwoFingersScaleMov多了一个controller, 该controller 主要是用于存储手势区分数据。

二、GestureDetector 深入了解

1.GestureDetector对象结构

如上图GestureDetector 将所需要的GestureRecognizer 进行组装,然后调用更为底层的RawGestureDetector 在该组件中完成手势检测。并回调GestureDetector 中注册的回调函数。

2.手势回调

3. Gesture Detector 代码片段

 
  Widget build(BuildContext context) {
    final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
    final DeviceGestureSettings? gestureSettings = MediaQuery.maybeGestureSettingsOf(context);

...
    if (onPanDown != null ||
        onPanStart != null ||
        onPanUpdate != null ||
        onPanEnd != null ||
        onPanCancel != null) {
      gestures[PanGestureRecognizer] = GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
        () => PanGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices),
        (PanGestureRecognizer instance) {
          instance
            ..onDown = onPanDown
            ..onStart = onPanStart
            ..onUpdate = onPanUpdate
            ..onEnd = onPanEnd
            ..onCancel = onPanCancel
            ..dragStartBehavior = dragStartBehavior
            ..gestureSettings = gestureSettings
            ..supportedDevices = supportedDevices;
        },
      );
    }

    if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) {
      gestures[ScaleGestureRecognizer] = GestureRecognizerFactoryWithHandlers<ScaleGestureRecognizer>(
        () => ScaleGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices),
        (ScaleGestureRecognizer instance) {
          instance
            ..onStart = onScaleStart
            ..onUpdate = onScaleUpdate
            ..onEnd = onScaleEnd
            ..dragStartBehavior = dragStartBehavior
            ..gestureSettings = gestureSettings
            ..trackpadScrollCausesScale = trackpadScrollCausesScale
            ..trackpadScrollToScaleFactor = trackpadScrollToScaleFactor
            ..supportedDevices = supportedDevices;
        },
      );
    }

 ...

    return RawGestureDetector(
      gestures: gestures,
      behavior: behavior,
      excludeFromSemantics: excludeFromSemantics,
      child: child,
    );
  }

三、封装

目标:封装目标是将双指缩放和双指移动共存手势作为一个新的手势控件,该控件为一个独立的插件,并且通过单独的插件工程进行维护。

1. 处理流程

分析GestureDetector 源码后,决定自行构建一个GestureDetector, 将 ScaleGestureRecognizer 回调设定内部回调(其他类型的GestureRecognizer 直接回调),先判断Gesture 类型,再行回调上层控件注册的回调函数。流程图如下:

2. 代码结构

对象说明
UserView上层使用手势控件
GestureDetectorTwoFingersScaleMov封装的支持双指缩放和双指移动共存手势,继承至 GestureDetector。没有完全重新写一个Detector 是因为GestureDetector 完成了资源管理,释放等相关操作,减少封装工作量。
TwoFingerScaleMoveController用于判断是缩放还是移动的数据存储控制器,该对象需要在上层代码中声明,并传给新的GestureDetector。因为GestureDetectorTwoFingersScaleMov 支持双指缩放移动以外的其他Gesture 上层控件可能会因为手势或是其他场景需要刷新页面,造成Detector重构。判定缩放的数据是一个累计量,不能因为Detector 重构而丢失数据。首次封装测试中,该controller 就是置于内部,从而出现缩放或是移动过程中,不断闪烁,就是应为其被不断重构。
TwoFingerScaleMoveGesture双指缩放和双指移动共存手势判定。ScaleGestureRecognizer 的回调,将会调用到该对象进行手势判定,再由该判定Gesture 调用上层注册的双指缩放或是双指移动回调

3. 回调变更

接口GestureDetectorGestureDetectorTwoFingersScaleMov说明
缩放开始onScaleStartonScaleMovStart
缩放中onScaleUpdateonScaleMovUpdateonScaleMovUpdate 回调时会返回缩放或是移动 Gesture类型,就算没有识别到具体类型,也会回调,只是Gesture 类型为Unknown
缩放结束onScaleEndonScaleMovEnd

四、使用

调用代码

 Widget createWidget() {
    return GestureDetectorTwoFingersScaleMov(
        controller: twoFingerScaleMoveController,

        // onScaleMoveStart
        onScaleMovStart: (details) {
          initScale = scale;
        },

        // scale or move update
        onScaleMovUpdate: (details) {
          // 0 unrecorgnize mode,1 zoom update, 2 moving update.
          if (details.type == TwoTuchMode.twoTouchZoomMode) {
            // once recorgnized as zoom or mov gesture this value is same until onScaleMovEnd
            scale = initScale * details.scale;
            debugPrint("details.scale:${details.scale}");
            setState(() {});
          } else if (details.type == TwoTuchMode.twoTouchMoveMode) {
            offset += details.focalPointDelta;
            setState(() {});
          }
        },

        // scale or move end
        onScaleMovEnd: (details) {
          debugPrint("onScaleMovEnd");
        },
        child: SizedBox.fromSize(
            size: MediaQuery.of(context).size,
            child: Stack(children: [
              const Text(
                'Two fingers zoom or move',
              ),
              Positioned(
                  top: offset.dy,
                  left: offset.dx,
                  child: Transform.scale(
                      key: _nodeKey,
                      scale: scale,
                      child: Image.asset("images/flutter.png"))),
            ])));
  }

上述代码中 onScaleMovUpdate 回调 details.type 会指定是未知,或是缩放或是移动。一旦认定为缩放或是移动,直到触控结束。
注意 在触控结束手指离开屏幕时,因为有先后顺序,所以会出现一个手指触控信息。新控件此时将details.type 设置回 unknown, 避免双指触控中途变成单指引起数据混乱。

效果

# 小结 ## 优势 封装后实现了与GestureDetector 相同的使用方式,同时以插件独立项目方式存在,便于长期维护和多项目使用。

劣势

不同版本Flutter 中因为Gesture 基类有差异,封装也要进行适当更新,需要针对不同版本进行适配。总体看,利大于弊。

本文源码

源码下载连接

系列

上一篇《双指缩放和双指移动共存手势系列–1方案》
下一篇 《双指缩放和双指移动共存手势系列–3发布》发布到pub.dev 稍后奉上

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

daimengliang

理性打赏,共同富裕!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值