Android开发中,完整的触摸事件会经历:手指按下(ACTION_DOWN)、手指移动(ACTION_MOVE)、和手指抬起(ACTION_UP),三个阶段,在Flutter中提供Listener组件来实现类似的功能。Android还提供了GestureDetector来帮助我们识别一些基本的触摸手势,如类似于:单击、双击、长按等操作,在Flutter中也提供了手势识别组件GestureDetector来实现类似的功能。

触摸监听 Listener

响应常见指针事件而调用回调的 widget

查看构造函数支持的属性:

const Listener({
    Key key,
    this.onPointerDown,// 按下手指回调
    this.onPointerMove,// 移动手指回调
    this.onPointerUp,// 抬起手指回调
    this.onPointerCancel,// 取消回调
    this.onPointerSignal,// 该对象在触摸发生时回调
    this.behavior = HitTestBehavior.deferToChild,// 命中测试期间的行为方式
    Widget child,// 子布局
  })
示例

居中显示的一个区域,触摸区域输出对应的坐标

在这里插入图片描述
示例伪代码如下:

Listener(
  child: Container(
    alignment: Alignment.center,
    color: Colors.blue,
    width: 300.0,
    height: 300.0,
  ),
  onPointerDown: (PointerDownEvent event){
    print("onPointerDown:${event.position.toString()}");
  },
  onPointerMove: (PointerMoveEvent event){
    print("onPointerMove:${event.position.toString()}");
  },
  onPointerUp: (PointerUpEvent event){
    print("onPointerUp:${event.position.toString()}");
  },
  onPointerSignal: (PointerSignalEvent event){
    print("onPointerSignal:${event.position.toString()}");
  },
  onPointerCancel: (PointerCancelEvent event){
    print("onPointerCancel:${event.position.toString()}");
  },
),

当触发指针事件时,参数PointerDownEvent、PointerMoveEvent、PointerUpEvent中的event会记录下触摸相关的信息。

这些信息来自PointerEvent,查看其构造函数中字段信息:

const PointerEvent({
    this.timeStamp = Duration.zero, // 事件调度的时间
    this.pointer = 0, // 指针的唯一标识符,不能重用
    this.kind = PointerDeviceKind.touch, //为其生成事件的输入设备的类型:touch(触摸设备),mouse(鼠标),stylus(手写笔),invertedStylus(手写笔),unknown(未知设备)
    this.device = 0, // 指示设备的唯一标识符,可在各种交互中重复使用
    this.position = Offset.zero, // 指针位置的坐标
    Offset localPosition, // 指针位置的本地坐标
    this.delta = Offset.zero, // 自上一个 PointerMoveEvent 或 PointerHoverEvent 后,指针移动的逻辑像素距离。
    Offset localDelta, // 事件接收者的本地坐标
    this.buttons = 0, // 使用 Button 常量的位字段
    this.down = false, // 设置指针当前是否向下。
    this.obscured = false, 
    this.pressure = 1.0, // 按压力度
    this.pressureMin = 1.0, // 按压力度最小值,该数字始终小于或等于1.0。
    this.pressureMax = 1.0, // 按压力度最大值,它将始终大于或等于1.0。
    this.distance = 0.0, // 被检测物体与输入表面的距离
    this.distanceMax = 0.0, // 如果此输入设备无法检测到“悬停触摸”输入事件,则为0.0。
    this.size = 0.0, // 屏幕被按下的区域。
    this.radiusMajor = 0.0, // 沿椭圆的接触椭圆的半径
    this.radiusMinor = 0.0, // 沿椭圆的接触椭圆的半径
    this.radiusMin = 0.0, // 可以为此指针报告 radiusMajor 和 radiusMinor 的最小值
    this.radiusMax = 0.0, // 可以为此指针报告 radiusMajor 和 radiusMinor 的最大值
    this.orientation = 0.0, // 指针移动方向,是一个角度值
    this.tilt = 0.0, // 被检测物体的倾斜角度
    this.platformData = 0, // 与事件关联的特定于平台的不透明数据。
    this.synthesized = false, // 设置事件是否由Flutter合成。
    this.transform, // 用于将该事件从全局坐标转换为事件接收者的坐标空间的转换。
    this.original, // 应用任何 transform 之前的原始未转换的 PointerEvent 
  }) 

手势识别 GestureDetector

检测手势的widget。被GestureDetector包裹的widget,可以很方便的监听到手势相关操作。如:单击、双击、长按等。

查看构造函数支持的属性:

  GestureDetector({
    Key key,
    this.child,
    this.onTapDown,// 点击按下时
    this.onTapUp,// 点击抬起时
    this.onTap,// 点击
    this.onTapCancel,// 点击取消
    this.onSecondaryTapDown,
    this.onSecondaryTapUp,
    this.onSecondaryTapCancel,
    this.onDoubleTap,// 双击
    this.onLongPress,// 长按
    this.onLongPressStart,//  长按开始
    this.onLongPressMoveUpdate,//  长按移动更新
    this.onLongPressUp,//  长按抬起
    this.onLongPressEnd,//  长按结束
    this.onVerticalDragDown,// 垂直移动回调,不能与 onScale,onPan,同时使用
    this.onVerticalDragStart,
    this.onVerticalDragUpdate,
    this.onVerticalDragEnd,
    this.onVerticalDragCancel, 
    this.onHorizontalDragDown,// 水平移动回调,不能与 onScale,onPan,同时使用
    this.onHorizontalDragStart,
    this.onHorizontalDragUpdate,
    this.onHorizontalDragEnd,
    this.onHorizontalDragCancel,
    this.onForcePressStart,// 按压力开始
    this.onForcePressPeak,// 按压力顶峰
    this.onForcePressUpdate,
    this.onForcePressEnd,
    this.onPanDown,// 移动按下,不能与 onScale,onVerticalDrag,onHorizontalDrag,同时使用
    this.onPanStart,// 移动开始
    this.onPanUpdate,
    this.onPanEnd,
    this.onPanCancel,
    this.onScaleStart,// 缩放开始,不能与 onPan ,onVerticalDrag,onHorizontalDrag,同时使用
    this.onScaleUpdate,
    this.onScaleEnd,
    this.behavior,// 此手势检测器在命中测试期间应如何表现。HitTestBehavior 枚举值:deferToChild,opaque,translucent
    this.excludeFromSemantics = false,// 是否从语义树中排除这些手势。
    this.dragStartBehavior = DragStartBehavior.start,// 确定处理拖动开始行为的方式。枚举值:down,start
  })

需要注意的是:

  1. onScale,onVerticalDrag,onHorizontalDrag 三个参数不能同时使用,
  2. onScale ,onPan不能同时使用
  3. onPan,onVerticalDrag,onHorizontalDrag 三个参数不能同时使用

否则会提示:Incorrect GestureDetector arguments. 参数冲突。

示例效果:
在这里插入图片描述

点击、双击、长按、水平滑动、垂直滑动

在蓝色区域内点击、双击、长按、水平滑动、垂直滑动在控制台可以看到回调信息输出。

在这里插入图片描述

跟随手指移动效果

在这里插入图片描述
实现思路就是在onPan回调里面处理widget的位置。
在这里插入图片描述

缩放效果

在这里插入图片描述
实现思路就是在onScale回调里面处理widget宽高
在这里插入图片描述

手势识别基类 GestureRecognizer

所有手势识别器都继承的基类。查看GestureDetector源码发现,GestureDetector的相关手势识别也是继承自该类。

例如双击回调监听使用到了DoubleTapGestureRecognizer
在这里插入图片描述

在之前的文章 Flutter 文本 Text 参数图文理解 中使用了TextSpan来拼接文本,并在特定的文本区域实现点击事件。

由于GestureDetector包裹的child必须是widget,而TextSpan并不是widget所以这里不能使用GestureDetector,但是TextSpan中提供了更为继承的手势识别类GestureRecognizer,通过该类我们可以实现手势监听。

使用实例

在这里插入图片描述
伪代码实现如下:
在这里插入图片描述

自定义手势识别

当有多个手势时,可能会产生冲突。如对图片进行点击、长按、缩放等操作的时候,如何识别用户当前是点击还是长按,缩放。如果想要精确地处理复杂交互手势,可自定义手势识别器来处理冲突问题。

如下示例:分别给红蓝背景的框框设置点击事件,但是点击蓝色背景时,底部的红色框框没有点击事件没有被识别。
在这里插入图片描述
上面的示例中想要红色框框也能识别手势事件,该怎么实现呢?

上面我们也介绍了指针的基本事件Listener,将第一个GestureDetector改为Listener组件,再来看一下效果:
在这里插入图片描述
可以看到使用Listener组件能够优先识别到触摸事件。但是想实现最外层的onTap事件,使用原始指针Listener组件来实现会比较复杂。

这时候可以使用自定义一个手势识别器来实现。
参考:Flutter核心技术与实战

在这里插入图片描述
wan~

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐