Flutter 双指缩放和双指移动共存手势检测系列之--1方案

本文介绍了在Flutter中如何处理双指缩放和双指移动的冲突问题,通过自定义GestureDetector实现手势判断,最终实现缩放和移动功能的独立操作。作者提供了源码和示例项目供读者参考。
摘要由CSDN通过智能技术生成

前言

本文所采用Flutter 版本为 3.10.6, 也许后续版本官方会对手势做进一步丰富完善,以解决本文涉及到的解决方案。

本文涉及项目手势需求:双指向外或内触屏伸缩,对图片进行缩放;双指在屏幕上同向触屏移动,移动图片。

关于Flutter缩放和移动

Flutter 使用中采用GestureDetector进行移动或是手势判定,该手势检测器内置了移动,缩放,长按等动作检测。如果应用在缩放和移动上是单手势,该控件能够满足应用需求。但是如果是个复合手势,如手势可能是缩放也可能是移动,然后就可能会有冲突。

本文就是基于实际项目中遇到的需要双指缩放和双指移动并存手势,在GestureDetector基础上构建一个双手指并存手势控件

最终效果

这是制作好后最终效果图:

应用商店搜索七彩涂色体验

GestureDetector 移动和缩放动作

  1. GestureDetector 移动和手势列表
动作回调冲突动作
垂直移动onVerticalDrag*水平移动
水平移动onVerticalDrag*垂直移动
移动onPan*缩放
缩放onScale*移动

如上表所示,缩放和移动动作是相互冲突。如果同时指定了缩放回调和移动回调,就会遇到如下图的异常。
缩放和移动冲突

  1. GestureDetector 双指缩放和移动共存测试结果
组合效果结论
只移动单双指均可行单指头拖拽图片和手势跟随很好,但是双指拖拽移动增量超过双指移动
缩放 + 移动直接异常查看源码,无法共存,抛出异常
缩放 + 水平移动或垂直移动不冲突,可移动缩放或移动内部自行断定,效果很好;但是只能水平移动或垂直移动
缩放 + 水平移动+垂直移动直接异常查看源码,水平移动和垂直移动无法共存,抛出异常

从上述测试表中可以看到要实现本文的双指缩放和移动共存,采用GestureDetector 不可实现。

双指缩放和移动Gesture方案

  1. 方案思路
    通过触摸点来进行来进行判断,如果触摸点之间距离在增加并大于阀值,则是缩放。如果触摸点之间距离相同,但是与起始点距离超过阀值,则是移动。

缩放示意图
缩放开始时,触点的中心位置基本保持不变。中心点相对不变则,但是触点距离变大,则是缩放动作。

移动示意图
移动开始时,触点之间相对距离不变,但是与起始点有较大位移。中心点位移,触摸点相对位置不变则是移动。

手势流程

2. 与**GestureDetector** 结合

本文中采用在GestureDetector 基础上实现双指缩放或移动,经过GestureDetector 源码查阅,决定在缩放回调基础上进行手指判断。因为缩放手势不仅对Scale 比例 进行了计算和回传,而且对触摸点位置也进行了回传。可以利用Scale 和缩放点判断,触摸点相对增加进行移动判断,减少工作量。

  1. 方案流程
  1. 效果
    按上述方案,对GestureDetector 更新后,测试效果缩放和移动均和手势跟随效果很好,如下图:

源码解读

GestureDetector(
          onScaleStart: (details) {
            if (details.pointerCount >= 2) {
              /// 清空手势判断类型参数
              offset = Offset.zero;
              scaleAccumulatedX = 0;
              scaleAccumulatedY = 0;
              twoTouchMode = TwoTuchMode.twoTouchNoneMode;
            }
          },
          onScaleUpdate: (details) {
            if (details.pointerCount >= 2) {
              if (twoTouchMode == TwoTuchMode.twoTouchNoneMode) {
                twoTouchMode = checkTwoTouchesMoveMode(details);
              }

              if (twoTouchMode == TwoTuchMode.twoTouchZoomMode) {
                debugPrint("details.scale:${details.scale}");
                scale = details.scale;
                setState(() {});
              } else if (twoTouchMode == TwoTuchMode.twoTouchMoveMode) {
                offset += details.focalPointDelta;
                setState(() {});
              }
            }
          },

上述代码在接收scaleUpdate 时,如果还没有识别到手势,则先调用 checkTwoTouchesMoveMode 进行判断。已经识别到手势,就按已识别的手势进行缩放处理。

    /// 判断手势类型是缩放还是移动
    /// 缩放类型判断阀值
    double constScaleThreshold = 0.1;
    /// 移动类型判断阀值
    double constPanningThreshold = 20;
    /// 类型判断,移动累计值
    double scaleAccumulatedX = 0;
    double scaleAccumulatedY = 0;
    TwoTuchMode checkTwoTouchesMoveMode(ScaleUpdateDetails touches) {
      if ((1 - touches.scale).abs() >= constScaleThreshold) {
        debugPrint("two touch scale mode:${(1 - touches.scale).abs()}");
        return TwoTuchMode.twoTouchZoomMode;
      }

      scaleAccumulatedX += touches.focalPointDelta.dx;
      scaleAccumulatedY += touches.focalPointDelta.dy;

      if (scaleAccumulatedX.abs() >= constPanningThreshold ||
          scaleAccumulatedY.abs() >= constPanningThreshold) {
        debugPrint(
            "two touch move mode dx:${scaleAccumulatedX.abs()} , dy:${scaleAccumulatedY.abs()}");
        return TwoTuchMode.twoTouchMoveMode;
      }

      return TwoTuchMode.twoTouchNoneMode;
    }

checkTwoTouchesMoveMode 进行缩放或是移动动作判断,缩放判断优先,如果超过阀值constScaleThreshold识别为缩放手势;横行或是纵向移动距离超过阀值 constPanningThreshold 识别为移动手势。

方案小结

小结

测试下来操作性还可以,代码在GestureDetector 基础上构建,也不复杂。

局限

代码没有进行过封装,与使用该缩放功能的代码混合编码,如果要跨项目使用,不便于功能更迭。

代码

本例代码在本文中全部打包提供。
环境:Flutter 3.10.6

示例项目体验

示例第一个截图,可以在应用商店搜索七彩涂色体验,由Flutter 构建的涂色应用。

本文源码

源码下载连接

系列

下一篇《Flutter 双指缩放和双指移动共存手势检测系列之–2封装》

  • 49
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
很高兴为您解答问题!关于Flutter实战视频-移动电商-48.详细页_详情和评论的切换,我可以提供以下解答: 在Flutter实战视频-移动电商-48中,详细页的详情和评论的切换是通过一个TabBar实现的。具体来说,通过在AppBar中添加一个TabBar来实现不同页面之间的切换。在这个TabBar中,我们可以添加两个Tab,分别为“详情”和“评论”,并分别对应两个页面。当用户点击不同的Tab时,就可以切换到不同的页面。 在Flutter中,TabBar可以使用DefaultTabController和TabBarView来实现。DefaultTabController是一个Widget,它可以管理TabBar和TabBarView之间的关系。TabBarView则是一个Widget,它可以显示不同的页面。 具体实现方法如下: ```dart class DetailPage extends StatefulWidget { @override _DetailPageState createState() => _DetailPageState(); } class _DetailPageState extends State<DetailPage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('商品详情'), bottom: TabBar( tabs: <Widget>[ Tab( text: '详情', ), Tab( text: '评论', ), ], ), ), body: DefaultTabController( length: 2, child: TabBarView( children: <Widget>[ // 详情页面 DetailWidget(), // 评论页面 CommentWidget(), ], ), ), ); } } ``` 在上面的代码中,我们首先创建了一个AppBar,并在其中添加了一个TabBar。然后,在Scaffold的body中,我们创建了一个DefaultTabController,并将其length设为2,即有两个页面。最后,我们在TabBarView中添加了两个Widget,即DetailWidget和CommentWidget,分别对应详情页面和评论页面。 希望这个解答能够对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

daimengliang

理性打赏,共同富裕!

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

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

打赏作者

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

抵扣说明:

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

余额充值