美文网首页
Flutter 之 手势识别 (四十八)

Flutter 之 手势识别 (四十八)

作者: maskerII | 来源:发表于2022-05-03 21:08 被阅读0次

1. GestureDetector

GestureDetector 定义

  GestureDetector({
    Key? key,
    this.child,
    this.onTapDown,
    this.onTapUp,
    this.onTap,
    this.onTapCancel,
    this.onSecondaryTap,
    this.onSecondaryTapDown,
    this.onSecondaryTapUp,
    this.onSecondaryTapCancel,
    this.onTertiaryTapDown,
    this.onTertiaryTapUp,
    this.onTertiaryTapCancel,
    this.onDoubleTapDown,
    this.onDoubleTap,
    this.onDoubleTapCancel,
    this.onLongPressDown,
    this.onLongPressCancel,
    this.onLongPress,
    this.onLongPressStart,
    this.onLongPressMoveUpdate,
    this.onLongPressUp,
    this.onLongPressEnd,
    this.onSecondaryLongPressDown,
    this.onSecondaryLongPressCancel,
    this.onSecondaryLongPress,
    this.onSecondaryLongPressStart,
    this.onSecondaryLongPressMoveUpdate,
    this.onSecondaryLongPressUp,
    this.onSecondaryLongPressEnd,
    this.onTertiaryLongPressDown,
    this.onTertiaryLongPressCancel,
    this.onTertiaryLongPress,
    this.onTertiaryLongPressStart,
    this.onTertiaryLongPressMoveUpdate,
    this.onTertiaryLongPressUp,
    this.onTertiaryLongPressEnd,
    this.onVerticalDragDown,
    this.onVerticalDragStart,
    this.onVerticalDragUpdate,
    this.onVerticalDragEnd,
    this.onVerticalDragCancel,
    this.onHorizontalDragDown,
    this.onHorizontalDragStart,
    this.onHorizontalDragUpdate,
    this.onHorizontalDragEnd,
    this.onHorizontalDragCancel,
    this.onForcePressStart,
    this.onForcePressPeak,
    this.onForcePressUpdate,
    this.onForcePressEnd,
    this.onPanDown,
    this.onPanStart,
    this.onPanUpdate,
    this.onPanEnd,
    this.onPanCancel,
    this.onScaleStart,
    this.onScaleUpdate,
    this.onScaleEnd,
    this.behavior,
    this.excludeFromSemantics = false,
    this.dragStartBehavior = DragStartBehavior.start,
  })
  • onTap 单击事件回调
  • onDoubleTap 双击事件回调
  • onLongPress 长按事件回调

2. GestureDetector 手势事件监听

2.1 单击 双击 长按

示例 1

GestureDetector对Container进行手势识别,触发相应事件后,在Container上显示事件名


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

  @override
  State<MSGestureDetectorDemo> createState() => _MSGestureDetectorDemoState();
}

class _MSGestureDetectorDemoState extends State<MSGestureDetectorDemo> {
  String _operation = "";
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("GestureDetectorDemo")),
      body: Center(
        child: GestureDetector(
          onTap: () => _updateText("Tap"),
          onDoubleTap: () => _updateText("DoubleTap"),
          onLongPress: () => _updateText("LongPress"),
          child: Container(
            width: 200,
            height: 200,
            color: Colors.amber,
            alignment: Alignment.center,
            child: Text(_operation),
          ),
        ),
      ),
    );
  }

  _updateText(String text) {
    setState(() {
      _operation = text;
    });
  }
}

image.png

注意: 当同时监听onTaponDoubleTap事件时,当用户触发tap事件时,会有200毫秒左右的延时,这是因为当用户点击完之后很可能会再次点击以触发双击事件,所以GestureDetector会等一段时间来确定是否为双击事件。如果用户只监听了onTap(没有监听onDoubleTap)事件时,则没有延时。

2.2 拖动、滑动

一次完整的手势过程是指用户手指按下到抬起的整个过程,期间,用户按下手指后可能会移动,也可能不会移动。GestureDetector对于拖动和滑动事件是没有区分的,他们本质上是一样的。GestureDetector会将要监听的组件的原点(左上角)作为本次手势的原点,当用户在监听的组件上按下手指时,手势识别就会开始。

示例 2


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

  @override
  State<MSGestureDetectorDemo2> createState() => _MSGestureDetectorDemo2State();
}

class _MSGestureDetectorDemo2State extends State<MSGestureDetectorDemo2> {
  double _left = 0.0;
  double _top = 0.0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("GestureDetectorDemo2")),
      body: Stack(
        children: [
          Positioned(
            top: _top,
            left: _left,
            child: GestureDetector(
              child: CircleAvatar(
                child: Text("A"),
              ),
              onPanDown: (DragDownDetails details) {
                print("onPanDown ${details.globalPosition}");
              },
              onPanUpdate: (DragUpdateDetails details) {
                print("onPanUpdate ${details.delta}");
                setState(() {
                  _top += details.delta.dy;
                  _left += details.delta.dx;
                });
              },
              onPanEnd: (DragEndDetails details) {
                print("onPanEnd ${details.velocity}");
              },
            ),
          ),
        ],
      ),
    );
  }
}

代码解释:

  • DragDownDetails.globalPosition:当用户按下时,此属性为用户按下的位置相对于屏幕(而非父组件)原点(左上角)的偏移。
  • DragUpdateDetails.delta:当用户在屏幕上滑动时,会触发多次Update事件,delta指一次Update事件的滑动的偏移量。
  • DragEndDetails.velocity:该属性代表用户抬起手指时的滑动速度(包含x、y两个轴的),示例中并没有处理手指抬起时的速度,常见的效果是根据用户抬起手指时的速度做一个减速动画。
98.gif

示例3 单一方向拖动


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

  @override
  State<MSGestureDetectorDemo3> createState() => _MSGestureDetectorDemo3State();
}

class _MSGestureDetectorDemo3State extends State<MSGestureDetectorDemo3> {
  var _top = 0.0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("GestureDetectorDemo3")),
      body: LayoutBuilder(
        builder: (context, constraints) {
          return Stack(
            children: [
              Positioned(
                top: _top,
                child: GestureDetector(
                  child: CircleAvatar(
                    child: Text("A"),
                  ),
                  // 垂直方向拖动事件
                  onVerticalDragUpdate: (DragUpdateDetails details) {
                    setState(() {
                      _top += details.delta.dy;
                      if (_top < 0) {
                        _top = 0;
                      }

                      if (_top > constraints.maxHeight) {
                        _top = constraints.maxHeight - 20;
                      }
                    });
                  },
                ),
              ),
            ],
          );
        },
      ),
    );
  }
}

这样就只能在垂直方向拖动了,如果只想在水平方向滑动同理

99.gif

2.3 缩放

GestureDetector可以监听缩放事件,下面示例演示了一个简单的图片缩放效果


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

  @override
  State<MSGestureDetectorDemo4> createState() => _MSGestureDetectorDemo4State();
}

class _MSGestureDetectorDemo4State extends State<MSGestureDetectorDemo4> {
  double _width = 200;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("GestureDetectorDemo4")),
      body: Center(
        child: GestureDetector(
          child: Image.asset(
            "assets/images/4.jpeg",
            fit: BoxFit.cover,
            width: _width,
          ),
          onScaleUpdate: (ScaleUpdateDetails details) {
            _width = 200 * details.scale.clamp(0.8, 5.0);
            setState(() {});
          },
        ),
      ),
    );
  }
}

100.gif

3. GestureRecognizer

GestureDetector内部是使用一个或多个GestureRecognizer来识别各种手势的,而GestureRecognizer的作用就是通过Listener来将原始指针事件转换为语义手势,GestureDetector直接可以接收一个子widget。GestureRecognizer是一个抽象类,一种手势的识别器对应一个GestureRecognizer的子类,Flutter实现了丰富的手势识别器,我们可以直接使用。

示例
假设我们要给一段富文本(RichText)的不同部分分别添加点击事件处理器,但是TextSpan并不是一个widget,这时我们不能用GestureDetector,但TextSpan有一个recognizer属性,它可以接收一个GestureRecognizer。


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

  @override
  State<MSGestureDetectorDemo5> createState() => _MSGestureDetectorDemo5State();
}

class _MSGestureDetectorDemo5State extends State<MSGestureDetectorDemo5> {
  TapGestureRecognizer _tapGestureRecognizer = TapGestureRecognizer();
  bool _toggle = false; // 变色开关
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("GestureDetectorDemo5")),
      body: Center(
        child: Text.rich(
          TextSpan(
            children: [
              TextSpan(text: "你好世界"),
              TextSpan(
                text: "点击变色",
                style: TextStyle(
                    fontSize: 30, color: _toggle ? Colors.red : Colors.blue),
                recognizer: _tapGestureRecognizer
                  ..onTap = () {
                    setState(() {
                      _toggle = !_toggle;
                    });
                  },
              ),
            ],
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _tapGestureRecognizer.dispose();
    super.dispose();
  }
}

101.gif

注意:使用GestureRecognizer后一定要调用其dispose()方法来释放资源(主要是取消内部的计时器)。

参考:https://book.flutterchina.club/chapter8/gesture.html

相关文章

网友评论

      本文标题:Flutter 之 手势识别 (四十八)

      本文链接:https://www.haomeiwen.com/subject/wayiyrtx.html