Flutter--GestureDetector手势识别组件

一、GestureDetector 的介绍

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.onLongPress,//长按事件回调
    this.onLongPressStart,//长按开始事件回调
    this.onLongPressMoveUpdate,//长按移动事件回调
    this.onLongPressUp,//长按抬起事件回调
    this.onLongPressEnd,//长按结束事件回调
    this.onSecondaryLongPress,
    this.onSecondaryLongPressStart,
    this.onSecondaryLongPressMoveUpdate,
    this.onSecondaryLongPressUp,
    this.onSecondaryLongPressEnd,
    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,
  }) : assert(excludeFromSemantics != null),
       assert(dragStartBehavior != null),
       assert(() {
         final bool haveVerticalDrag = onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null;
         final bool haveHorizontalDrag = onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null;
         final bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null;
         final bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
         if (havePan || haveScale) {
           if (havePan && haveScale) {
             throw FlutterError.fromParts(<DiagnosticsNode>[
               ErrorSummary('Incorrect GestureDetector arguments.'),
               ErrorDescription(
                 'Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan.'
               ),
               ErrorHint('Just use the scale gesture recognizer.')
             ]);
           }
           final String recognizer = havePan ? 'pan' : 'scale';
           if (haveVerticalDrag && haveHorizontalDrag) {
             throw FlutterError(
               'Incorrect GestureDetector arguments.\n'
               'Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a $recognizer gesture recognizer '
               'will result in the $recognizer gesture recognizer being ignored, since the other two will catch all drags.'
             );
           }
         }
         return true;
       }()),
       super(key: key);

三、GestureDetector的属性介绍

 onTapDown  按下时回调

onTapUp  抬起时回调

onTap  点击事件回调

onTapCancel 点击取消事件回调

onDoubleTapDown 短时间内双击按下时回调

onDoubleTap 短时间内双击回调

onDoubleTapCancel 短时间内双击取消事件回调

onLongPress 长按事件回调

onLongPressStart 长按开始事件回调

onLongPressMoveUpdate 长按移动事件回调

onLongPressUp 长按抬起事件回调

onLongPressEnd 长按结束事件回调

onVerticalDragDown 垂直滑动按下事件回调

onVerticalDragStart 垂直滑动开始事件回调

onVerticalDragUpdate 垂直滑动更新事件回调

onVerticalDragEnd 垂直滑动结束事件回调

onVerticalDragCancel 垂直滑动取消事件回调

onHorizontalDragDown 水平滑动按下事件回调

onHorizontalDragStart 水平滑动开始事件回调

onHorizontalDragUpdate 水平滑动更新事件回调

onHorizontalDragEnd 水平滑动结束事件回调

onHorizontalDragCancel 水平滑动取消事件回调

onPanDown 按压时回调(我自己理解为点击事件,但是比onTap优先级高,onPan先执行先取消,onPan比onTap多个滑动监听)

onPanStart 按压拖动开始回调,按压开始,不能与 onScale ,onVerticalDrag,onHorizontalDrag,同时使用 onPanUpdate 按压拖动回调 onPanEnd 按压拖动结束回调

onPanCancel 按压拖动取消回调

onScaleStart 缩放开始事件回调缩放开始,不能与 onPan ,onVerticalDrag,onHorizontalDrag,同时使用

onScaleUpdate 缩放更新事件回调

onScaleEnd 缩放结束事件回调

behavior 手势检测器在触摸中应该如何工作
HitTestBehavior.deferToChild:只有当前容器中的child被点击时才会响应点击事件
HitTestBehavior.opaque:点击整个区域都会响应点击事件,但是点击事件不可穿透向下传递,注释翻译:阻止视觉上位于其后方的目标接收事件。
HitTestBehavior.translucent:同样是点击整个区域都会响应点击事件,和opaque的区别是点击事件是否可以向下传递,注释翻译:半透明目标既可以在其范围内接受事件,也可以允许视觉上位于其后方的目标接收事件

四、一些触摸事件,监听的调用顺序

4.1、点击屏幕:onPanDown--onPanCancel--onTapDown--onTapUp--onTap

4.2、点击滑动:onPanDown--onTapDown--onTapCancel--onPanStart--onPanUpdate--onPanEnd

4.3、双击屏幕:onPanDown--onPanCancel--onDoubleTapDown--onPanDown--onPanCancel--onDoubleTap

4.4、长按屏幕:onPanDown--onTapDown--onTapCancel--onPanCancel--onLongPressStart--onLongPress--onLongPressMoveUpdate--onLongPressEnd--onLongPressUp

4.5、手指左右滑动:onVerticalDragDown--onHorizontalDragDown--onVerticalDragCancel--onHorizontalDragStart--onHorizontalDragUpdate--onHorizontalDragEnd

4.6、手指上下滑动:onVerticalDragDown--onHorizontalDragDown--onHorizontalDragCancel--onVerticalDragStart--onVerticalDragUpdate--onVerticalDragEnd

4.7、缩放:onScaleStart--onScaleUpdate--onScaleEnd

五、GestureDetector的demo

5.1、给组件添加按键监听

class _GestureDetectorFulState extends State<GestureDetectorFul> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(
        title: Text("GestureDetector学习"),
      ),
      body: Center(
        child: GestureDetector(
          //一定要加(按钮和文字之间的空白,也添加监听了)
          behavior: HitTestBehavior.opaque,
          onPanDown: (value) {
            print("我是onPanDown");
          },
          child: Row(
            children: [
              OutlinedButton(
                child: Text("按钮"),
                onPressed: () {
                  print("点击了button");
                },
              ),
              SizedBox(width: 80),
              Text("文字")
            ],
          )
        ),
      )
    ));
  }
}

Flutter--GestureDetector手势识别组件 - 简书一、GestureDetector 的介绍 GestureDetector:是手势识别的组件,可以识别点击、双击、长按事件、拖动、缩放等手势 二、GestureDetecto...https://www.jianshu.com/p/c338c7975f30

Flutter手势操作 —全局坐标与局部坐标的获取

全局坐标与局部坐标

在GestureDetector中有两个重要属性值globalPositionlocalPosition,两者都是Offset对象
globalPosition就像它的命名表示当前手势触点在**全局坐标系位置对应组件顶点坐标的偏移量(dx,dy)
localPosition则就表示当前手势触点在
对应组件坐标系位置对应组件顶点坐标的偏移量(dx,dy)**。

例如如下代码中,为Container设置了GestureDetector手势监听,在update回调中获取updateDetail对象,在Text中显示globalPosition偏移量。从中获取到的globalPosition和localPosition中dx的值相同,dy却不同。也就是因为Scaffold中设置了AppBar,相对于body他的全局坐标系并非它自身。但若将Scaffold中的AppBar去除,让body撑满整个Scaffold,那么在手势监听中获取到的globalPosition和localPosition偏移量将相同。

需要注意的是对于globalPosition在安卓中还包含了手机状态栏的高度。

通过DRAGUPDATEDETAILS获取全局坐标和局部坐标

DragUpdateDetails 相当网页前端的Evnet事件,可以获取当前触摸点的全局坐标和局部坐标。

  onPanUpdate: (DragUpdateDetails detail) {   
    //获取当前触摸点的全局坐标
    var globalPosition=detail.globalPosition;
    //获取当前触摸点的局部坐标
    var localPosition=detail.localPosition;
   }

通过CONTEXNT.FINDRENDEROBJECT()获取全局坐标和局部坐标

当前的页面结构(PainterPage):

  • Scafford

    • body

      • GestureDetector //手势Widget
        • onPanUpdate(detail)
onPanUpdate:(detail){
  final RenderBox Box = context.findRenderObject();   									// 获取的对象为当前页面对象,PainterPage
  Offset localPosition = Box.globalToLocal(detail.globalPosition);			// 转换为局部坐标但实际是全局坐标
  Offset globalPosition = Box.localToGlobal(detail.globalPosition);			// 获得的是全局坐标。
}

为甚么通过这样的方式,不能正确获取到局部坐标呢?

原因在于context.findRenderObject(),通过context,获取到的Box,实际上是PaintPage本身。所以,对于Box自身来说全局坐标局部坐标是一致的。

 

而实际上我们真正想要的是GestureDetector里的坐标系。由前面我们可以知道context.findRenderObject()只能获取context当前所在页面的页面 Widget

为了解决不能获取到GuesterDetector的坐标的问题,我们需要将GuesterDetector封装成一个StatefulWidget或者一个StatelessWidget,即GuesterDetectorWidget。再将GuesterDetectorWidget引入到PainterPage

更详细的说明可以参详字母索引快速定位

onPanUpdate:(detail){
  final RenderBox Box = context.findRenderObject();   									// 获取的对象为当前页面对象,PainterPage
  Offset localPosition = Box.globalToLocal(detail.globalPosition);			// 转换为局部坐标
  Offset globalPosition = Box.localToGlobal(detail.globalPosition);			// 获得的是全局坐标。
}

MaterialApp(
      theme: AppTheme.themes[store.state.appThemeState.themeType],
      home: Scaffold(
        appBar: AppBar(),
        body: GestureDetector(
          onPanStart: (detail) {
            showLog(detail.runtimeType, detail.localPosition,
                detail.globalPosition);
          },
          onPanUpdate: (detail) {
            showLog(detail.runtimeType, detail.localPosition,
                detail.globalPosition);
            setState(() {
              offsetText = "globalPosition: ${Offset(detail.globalPosition.dx, detail.globalPosition.dy).toString()} \n"
                  "localPosition: ${Offset(detail.localPosition.dx, detail.localPosition.dy).toString()}";
            });
          },
          onPanEnd: (detail) {
            setState(() {
              offsetText = "end";
            });
          },
          child: Container(
            color: Colors.red,
            width: double.infinity,
            height: double.infinity,
            child: Center(
              child: Text(
                 offsetText,
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 25,
                ),
              ),
            ),
          ),
        ),
      ),
    );

 可以看到当Scaffold包含和未包含AppBar时,Container两个偏移量输出的差异

 

 
#### 知识地图

  • Flutter的坐标体系是以Widget的左上角为原点,向左为正x轴,以下为正y轴;

  • 全局坐标是整个屏幕的左上角开始计算的

  • 局部坐标,是以当前GestureDetector包裹的Wiget的左上角为原点开始计算的

参考资料:

Flutter实战之手势操作篇

实现 View 的移动拖拽

flutter 白板工具

我们通过for循环建立的控件的时候,通过点击判断索引值跳转到不同页面

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值