Flutter 交互基础之 GestureDetector 手势

介绍

GestureDetector是Flutter的手势检测器,它会尝试识别与其非null的回调相对应的手势。若是此Widget组件具备子控件,那么它的大小调整行为将听从该子控件件。若是它没有子控件,那么它将变大以适合父控件。git

默认状况下,带有不可见子控件的手势检测器会忽略触摸;能够经过行为控制此逻辑。GestureDetector还监听可访问性事件,并将其映射到callback回调。若要忽略可访问性事件,请将ExcludeFromSemantics设置为true。github

Flutter中的手势系统有两个独立的层。第一层为原始指针(pointer)事件,它描述了屏幕上指针(例如,触摸、鼠标和触控笔)的位置和移动。 第二层为手势,描述由一个或多个指针移动组成的语义动做,如拖拽、缩放、双击、长按等。bash

手势

在flutter中,手势表示能够从多个单独的指针事件(甚至多是多个单独的指针)识别的语义动做(例如点击,拖拽和缩放)。 完整的一个手势能够分发多个事件,对应于手势的生命周期(例如,拖拽开始,拖拽更新和拖拽结束):app

  • 单击less

    • onTapDown 指针已经在特定位置与屏幕接触
    • onTapUp 指针中止在特定位置与屏幕接触
    • onTap 单击事件触发
    • onTapCancel 先前指针触发的onTapDown不会在触发单击事件
  • 双击ide

    • onDoubleTap 用户快速连续两次在同一位置轻敲屏幕.
  • 长按函数

    • onLongPress 指针在相同位置长时间保持与屏幕接触
  • 垂直拖拽工具

    • onVerticalDragStart 指针已经与屏幕接触并可能开始垂直移动
    • onVerticalDragUpdate 指针与屏幕接触并已沿垂直方向移动.
    • onVerticalDragEnd 先前与屏幕接触并垂直移动的指针再也不与屏幕接触,而且在中止接触屏幕时以特定速度移动
  • 水平拖拽ui

    • onHorizontalDragStart 指针已经接触到屏幕并可能开始水平移动
    • onHorizontalDragUpdate 指针与屏幕接触并已沿水平方向移动
    • onHorizontalDragEnd 先前与屏幕接触并水平移动的指针再也不与屏幕接触,并在中止接触屏幕时以特定速度移动

继承关系

Object —> Diagnosticable —> DiagnosticableTree —> Widget —> StatelessWidget —> GestureDetectorspa

构造函数

Draggable({Key key, 
 @required Widget child,
 @required Widget feedback,
 T data, 
 Axis axis,
 Widget childWhenDragging,
 Offset feedbackOffset: Offset.zero, 
 DragAnchor dragAnchor: DragAnchor.child, 
 Axis affinity, 
 int maxSimultaneousDrags,
 VoidCallback onDragStarted, 
 DraggableCanceledCallback onDraggableCanceled, 
 DragEndCallback onDragEnd, 
 VoidCallback onDragCompleted, 
 bool ignoringFeedbackSemantics: true }) 
复制代码

经常使用属性

  • dragStartBehavior → DragStartBehavior 肯定处理拖拽开始行为的方式

  • excludeFromSemantics → bool 是否从语义树中排除这些手势。

    例如,用于显示工具提示的长按手势被排除,由于工具提示自己直接包含在语义树中,所以具备显示它的手势将致使信息的重复。

  • onDoubleTap → GestureTapCallback 用户已快速连续两次在同一位置使用主按钮轻触屏幕。

  • onForcePressEnd → GestureForcePressEndCallback 指针再也不与屏幕接触。

  • onForcePressPeak → GestureForcePressPeakCallback 指针与屏幕接触并以最大力按下。力量至少是 ForcePressGestureRecognizer.peakPressure。

  • onForcePressStart → GestureForcePressStartCallback 指针与屏幕接触,并用足够的力按压以启动压力。力量至少是 ForcePressGestureRecognizer.startPressure。

  • onForcePressUpdate → GestureForcePressUpdateCallback 指针与屏幕接触,以前已经经过了 ForcePressGestureRecognizer.startPressure,而且要么在屏幕的平面上移动,要么用不一样的力按压屏幕,要么同时按两个屏幕。

  • onHorizontalDragCancel → GestureDragCancelCallback 先前触发onHorizontalDragDown的指针未完成触发了取消。

  • onHorizontalDragDown → GestureDragDownCallback 指针已经过主按钮与屏幕接触,并可能开始水平移动。

  • onHorizontalDragEnd → GestureDragEndCallback 以前与主屏幕接触而且水平移动的指针再也不与屏幕接触,而且当它中止接触屏幕时以特定速度移动。

  • onHorizontalDragStart → GestureDragStartCallback 指针已经过主按钮与屏幕接触,并开始水平移动。

  • onHorizontalDragUpdate → GestureDragUpdateCallback 与主按钮接触而且水平移动的指针在水平方向上移动。

  • onLongPress → GestureLongPressCallback 当识别出具备主按钮的长按手势时调用。

  • onLongPressEnd → GestureLongPressEndCallback 使用主按钮触发长按的指针已中止接触屏幕。

  • onLongPressMoveUpdate → GestureLongPressMoveUpdateCallback 使用主按钮长按后,指针已被拖动。

  • onLongPressStart → GestureLongPressStartCallback 当识别出具备主按钮的长按手势时调用。

  • onLongPressUp → GestureLongPressUpCallback 使用主按钮触发长按的指针已中止接触屏幕。

  • onPanCancel → GestureDragCancelCallback 先前触发onPanDown的指针未完成。

  • onPanDown → GestureDragDownCallback 指针已经过主按钮与屏幕接触,并可能开始移动。

  • onPanEnd → GestureDragEndCallback 先前经过主按钮与屏幕接触而且移动的指针再也不与屏幕接触,而且当它中止接触屏幕时以特定速度移动。

  • onPanStart → GestureDragStartCallback 触摸点与屏幕接触,并已开始移动。

  • onPanUpdate → GestureDragUpdateCallback 屏幕上的触摸点位置每次改变时,都会触发该回调。

  • onScaleEnd → GestureScaleEndCallback 指针再也不与屏幕接触。

  • onScaleStart → GestureScaleStartCallback 与屏幕接触的指针已创建焦点,初始比例为1.0。

  • onScaleUpdate → GestureScaleUpdateCallback 与屏幕接触的指针表示新的焦点和/或比例。

  • onSecondaryTapCancel → GestureTapCancelCallback 先前触发onSecondaryTapDown的指针不会最终致使点击。

  • onSecondaryTapDown → GestureTapDownCallback 可能致使使用辅助按钮敲击的指针已在特定位置与屏幕联系。

  • onSecondaryTapUp → GestureTapUpCallback 将触发带有辅助按钮的敲击的指针已中止在特定位置接触屏幕。

  • onTap → GestureTapCallback 带主按钮的点击事件触发源头。

  • onTapCancel → GestureTapCancelCallback 先前触发onTapDown的指针不会致使点击。

  • onTapDown → GestureTapDownCallback 可能致使使用主按钮敲击的指针已在特定位置与屏幕联系。

  • onTapUp → GestureTapUpCallback 将触发带主按钮的敲击的指针已中止在特定位置接触屏幕。

  • onVerticalDragCancel → GestureDragCancelCallback 先前触发onVerticalDragDown的指针未完成。

  • onVerticalDragDown → GestureDragDownCallback 指针已经过主按钮与屏幕接触,并可能开始垂直移动。

  • onVerticalDragEnd → GestureDragEndCallback 先前与主屏幕接触而且垂直移动的指针再也不与屏幕接触,而且当它中止接触屏幕时以特定速度移动。

  • onVerticalDragStart → GestureDragStartCallback 指针已经过主按钮与屏幕接触,并已开始垂直移动。

  • onVerticalDragUpdate → GestureDragUpdateCallback 与主按钮接触而且垂直移动的指针在垂直方向上移动。

经常使用方法

  • build(BuildContext context) → Widget 建立组件。
  • debugFillProperties(DiagnosticPropertiesBuilder 属性) →void 添加与节点关联的其余属性。
  • createElement() → StatelessElement 建立StatelessElement以管理此组件在UI树中的位置。

使用示例

1. 单击、双击、长按事件

这里经过一个demo,用GestureDetector对Container组件进行手势识别。在触发相应事件后,Container上显示事件名,为了增大点击区域,将Container设置为200×200,代码以下:

import 'package:flutter/material.dart';

class GestureDetectorTestRoute extends StatefulWidget {
  @override
  _GestureDetectorTestRouteState createState() =>
      new _GestureDetectorTestRouteState();
}

class _GestureDetectorTestRouteState extends State<GestureDetectorTestRoute> {
  String _operation = "No Gesture detected!"; 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text("GestureDetectorTest"),
      ),
      body: Center(
        child: GestureDetector(
          child: Container(
            alignment: Alignment.center,
            color: Colors.blue,
            width: 200.0,
            height: 200.0,
            child: Text(
              _operation,
              style: TextStyle(
                  color: Colors.white,
                  fontSize: 14,
                  decoration: TextDecoration.none),
            ),
          ),
          onTap: () => updateText("onTap"), //单击
          onDoubleTap: () => updateText("onDoubleTap"), //双击
          onLongPress: () => updateText("onLongPress"), //长按
        ),
      ),
    );
  }

  //更新文本
  void updateText(String text) {
    setState(() {
      _operation = text;
    });
    //提示
    showSnackBar(text);
  }

  var _scaffoldKey = new GlobalKey<ScaffoldState>();

  void showSnackBar(String message) {
    var snackBar = SnackBar(
        content: Text(message),
        backgroundColor: Colors.lightGreen,
        duration: Duration(milliseconds: 400));
    _scaffoldKey.currentState.showSnackBar(snackBar);
  }
}

复制代码

效果图以下:

GestureDetector

注意: 若同时监听onTap和onDoubleTap事件,当用户触发tap事件时,会有200毫秒左右的延时。这是由于当用户点击完以后极可能会再次点击以触发双击事件,因此GestureDetector源码内部会等200毫秒时间来肯定是否为双击事件。若是用户只监听了onTap(没有监听onDoubleTap)事件则回调没有延时。

2. 拖拽、滑动事件

import 'package:flutter/material.dart';

class DragTest extends StatefulWidget {
  @override
  _DragTestState createState() => new _DragTestState();
}

class _DragTestState extends State<DragTest>
    with SingleTickerProviderStateMixin {
  double _top = 0.0; //距顶部的偏移
  double _left = 0.0; //距左边的偏移

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("DragTest"),
      ),
      body: Stack(
        children: <Widget>[
          Positioned(
            top: _top,
            left: _left,
            child: GestureDetector(
              child: CircleAvatar(
                  child: Text("Draggable Text", textAlign: TextAlign.center),
                  radius: 50),
              //手指按下时会触发此回调
              onPanDown: (DragDownDetails e) {
                //打印手指按下的位置(屏幕)
                print("用户手指按下:${e.globalPosition}");
              },
              //手指滑动时会触发此回调
              onPanUpdate: (DragUpdateDetails e) {
                //用户手指滑动时,更新偏移,从新构建
                setState(() {
                  _left += e.delta.dx;
                  _top += e.delta.dy;
                });
              },
              onPanEnd: (DragEndDetails e) {
                //打印滑动结束时在x、y轴上的速度
                print(e.velocity);
              },
            ),
          )
        ],
      ),
    );
  }
}

复制代码

效果图以下:

Drag

3. 缩放事件

除了单击/双击/拖拽等事件,GestureDetector能够监听缩放事件。下面咱们演示一个简单的图片缩放效果,示例代码以下:

class _ScaleTestRouteState extends State<_ScaleTestRoute> {
  double _width = 200.0; //经过修改图片宽度来达到缩放效果

  @override
  Widget build(BuildContext context) {
   return Center(
     child: GestureDetector(
        //指定宽度,高度自适应
        child: Image.asset("./images/sea.png", width: _width),
        onScaleUpdate: (ScaleUpdateDetails details) {
          setState(() {
            //缩放倍数在0.8到10倍之间
            _width=200*details.scale.clamp(.8, 10.0);
          });
        },
      ),
   );
  }
}
复制代码

效果图以下:

Scale

总结

经过这篇文章,咱们了解了flutter 所提供的GestureDetector手势检测器的一些基本概念及能力,它内部封装了诸多API,让咱们能够很高效快速开发应用。经过文章的内容讲述,咱们知道如何去监听单击/双击/拖拽等事件及处理用户的交互逻辑。固然,触摸交互模型里面不可避免地会出现事件竞争和事件冲突问题,本篇文章因为篇幅问题就没有进行讲解了,下篇文章咱们将会侧重讲述flutter中的事件竞争及冲突问题。

做者


xiaosongzeem
相关文章
相关标签/搜索