前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《Flutter》-- 7.事件处理

《Flutter》-- 7.事件处理

作者头像
爱学习的程序媛
发布2022-04-07 16:23:51
1.9K0
发布2022-04-07 16:23:51
举报
文章被收录于专栏:学习/读书笔记

参阅书籍:

《Flutter跨平台开发入门与实践》-- 向治洪(著)

7. 事件处理

7.1 原始指针事件

7.1.1 基本概念

一个完整的原始指针事件主要由手指按下、手指移动、手指抬起以及触摸取消构成,更高基本的手势都基于这些原始事件。

在Flutter的原始指针事件模型中,在手指接触屏幕发起触摸事件时,Flutter会首先确定手指与屏幕发生接触的位置上究竟有哪些组件,然后通过命中测试(Hit Test)交给最内层的组件去响应。

Flutter无法像浏览器冒泡那样取消或者停止事件的进一步分发,只能通过执行命中测试去调整组件的事件触发时机。

PointerDownEvent、PointerMoveEvent和PointerUpEvent是Flutter的原始指针事件的基本组成部分,分别对应手指按下、移动和抬起事件,它们都是PointerEvent的子类。

在Flutter的事件模型中PointerEvent是Flutter原始指针事件的基础类,可以用它获取当前指针的一些信息:

1)position:全局坐标的偏移量;

2)delta:两次指针移动事件的距离;

3)pressure:按压力度,如果手机屏幕支持压力传感器,此属性会返回压力值,如果手机不支持则始终返回1;

4)orientation:指针移动方向,是一个角度值。

对于组件层面的原始指针事件的监听,Flutter提供了一个Listener,可以用它监听包裹的子组件的原始指针事件。

代码语言:javascript
复制
Listener(
  onPointerDown: (downPointEvent) {
    //按下回调
    ...
  },
  onPointerMove: (movePointEvent) {
    //移动回调
    ...
  },
  onPointerUp: (upPointEvent) {
    //抬起回调
    ...
  },
  child: Container(
    child: Text(‘Listener事件监听’);
  )
)

原始指针事件还提供了behavior属性,它决定子组件如何响应命中测试,它的值类型为HitTestBehavior,是一个枚举类型,有3个枚举值:

1)deferToChild:子组件一个接一个地进行命中测试,如果子组件中有通过命中测试的,则当前组件会收到指针事件,并且其父组件会收到指针事件;

2)opaque:在进行命中测试时,当前组件会被当成不透明进行处理,单击的响应区域即为单击区域;

3)translucent:设置此属性后,组件自身和底部可视区域都能够响应命中测试,即点击顶部组件时,顶部组件和底部组件都可以接收到指针事件。

7.1.2 忽略事件

如果不想让某个子组件响应原始指针事件,可以使用AbsorbPointer或IgnorePointer组件包裹子组件来阻止子组件接收指针事件。

AbsorbPointer组件会参与命中测试,它本身可以接收指针事件,其包裹的子组件不能;而IgnorePointer组件不会参与命中测试,它完全不能接收指针事件。

代码语言:javascript
复制
import 'package:flutter/material.dart';

void main() => runApp(PointerWidget());

class PointerWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '事件处理',
      home: Scaffold(
        appBar: AppBar(title: Text('事件处理')),
        body: Listener(
          child: AbsorbPointer(
            child: Listener(
              child: Container(
                color: Colors.red,
                width: 200,
                height: 200,
              ),
              onPointerDown: (e) => print('point in'),
            )
          ),
          onPointerDown: (e) => print('point up'),
        ) 
      )
    );
  }
}

7.2 手势识别组件

7.2.1 基本用法

在Flutter开发中,Gesture API代表手势语义的抽象,从组件层面监听手势可以使用GestureDetector等手势响应组件。

GestureDetector组件是一个处理各种高级用户触摸行为的组件,使用时只需要将它作为父组件包裹在其他子组件外面即可。

7.2.2 常用事件

GestureDetector常用事件:

如果同时监听onTap和onDoubleTap事件时,onTap事件会有200ms左右的延迟。

示例代码:

代码语言:javascript
复制
import 'package:flutter/material.dart';

void main() => runApp(GestureDetectorPage());

class GestureDetectorPage extends StatefulWidget {
  State<StatefulWidget> createState() {
    return GestureDetectorPageState();
  }
}

class GestureDetectorPageState extends State<GestureDetectorPage> {
  String operation = 'No Gesture';

  void updateGesture(String text) {
    setState(() => operation = text);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '事件处理',
      home: Scaffold(
        appBar: AppBar(title: Text('事件处理 -- $operation')),
        body: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GestureDetector(
              child: Container(
                margin: EdgeInsets.only(top: 20),
                alignment: Alignment.center,
                color: Colors.blue,
                width: 150,
                height: 80,
                child: Text(
                  operation,
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 24,
                  ),
                ),
              ),
              onTap: () => updateGesture('Tap'),
              onDoubleTap: () => updateGesture('DoubleTap'),
              onLongPress: () => updateGesture('LongPress'),
            )
          ],
        )
      )
    );
  }
}

示例效果:

7.2.3 拖拽与缩放

在处理拖拽事件时,GestureDetector会将需要监听组件的原点作为本次手势的起点,当用户在监听组件上按下手指时手势识别就开始运行。

示例代码:

代码语言:javascript
复制
import 'package:flutter/material.dart';

void main() => runApp(DragPage());

class DragPage extends StatefulWidget {
  State<StatefulWidget> createState() {
    return DragState();
  }
}

class DragState extends State<DragPage> {
  String operation = 'No Gesture';

  void updateGesture(String text) {
    setState(() => operation = text);
  }
  double _top = 0.0;
  double _left = 0.0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '事件处理--拖拽',
      home: Scaffold(
        appBar: AppBar(title: Text('事件处理--拖拽')),
        body: Stack(
          children: <Widget>[
            Positioned(
              top: _top,
              left: _left,
              child: GestureDetector(
                child: CircleAvatar(
                  radius: 30,
                  backgroundColor: Colors.blue,
                  child: Text(
                    'Drag',
                    style: TextStyle(color: Colors.white),
                  ),
                ),
                onPanDown: (DragDownDetails e) => print('onPanDown: ${e.globalPosition}'),
                onPanUpdate: (DragUpdateDetails e) {
                  setState(() {
                     _left += e.delta.dx;
                     _top += e.delta.dy;
                  });
                },
                onPanEnd: (DragEndDetails e) => print('onPanEnd: ${e.velocity.toString()}'),
              ),
            )
          ],
        )
      )
    );
  }
}

示例效果:

如果只需要沿一个方向拖动,可以将onPanUpdate改为onVerticalDragUpdate或者onHorizontalDragUpdate。

可以使用GestureDetector组件的onScaleUpdate实现缩放效果。

示例代码:

代码语言:javascript
复制
import 'package:flutter/material.dart';

void main() => runApp(ScalePage());

class ScalePage extends StatefulWidget {
  State<StatefulWidget> createState() {
    return ScaleState();
  }
}

class ScaleState extends State<ScalePage> {
  double width = 100.0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '事件处理--缩放',
      home: Scaffold(
        appBar: AppBar(title: Text('事件处理--缩放')),
        body: Center(
          child: GestureDetector(
            child: Image.asset('images/qq.png', width: width),
            onScaleUpdate: (ScaleUpdateDetails details) {
              setState(() {
                width = 200 * details.scale.clamp(0.8, 10.0);
              });
            },
          ),
        )
      )
    );
  }
}

7.2.4 手势识别器

GestureDetector封装了Listener的原始指针事件,可以很容易地对各种手势进行识别。GestureDetector是一个抽象类,有多个实现子类,通常一种手势识别器即对应一个GestureDetector的实现类。

示例代码:动态改变富文本文字大小

代码语言:javascript
复制
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

void main() => runApp(GestureRecognizerPage());

class GestureRecognizerPage extends StatefulWidget {
  State<StatefulWidget> createState() {
    return GestureRecognizerState();
  }
}

class GestureRecognizerState extends State<GestureRecognizerPage> {
  TapGestureRecognizer recognizer = TapGestureRecognizer();
  bool toggle = false;

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '事件处理--手势识别器',
      home: Scaffold(
        appBar: AppBar(title: Text('事件处理--手势识别器')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('点击文字放大'),
              Text.rich(
                TextSpan(
                  text: '你好,Flutter',
                  style: TextStyle(fontSize: toggle ? 30 : 60),
                  recognizer: recognizer..onTap = () {
                    setState(() {
                      toggle = !toggle;
                    });
                  }
                ), 
              )
            ],
          )
        )
      )
    );
  }
}

示例效果:

使用手势识别器后一定要调用dispose()来释放资源,因为手势识别器内部使用了计时器,不释放的话会造成大量的资源消耗。

7.2.5 手势竞争

对于需要处理多个手势识别的场景,Flutter引入了手势竞技场的概念,用来识别究竟哪个手势最终响应用户事件。手势竞技场通过综合对比用户触摸屏幕的时长、位移以及拖拽方向来确定最终手势。

示例代码:

代码语言:javascript
复制
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

void main() => runApp(GestureCompetePage());

class GestureCompetePage extends StatefulWidget {
  State<StatefulWidget> createState() {
    return GestureCompeteState();
  }
}

class GestureCompeteState extends State<GestureCompetePage> {
  double _top = 0.0;
  double _left = 0.0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '事件处理--手势竞争',
      home: Scaffold(
        appBar: AppBar(title: Text('事件处理--手势竞争')),
        body: Stack(
          children: <Widget>[
            Positioned(
              top: _top,
              left: _left,
              child: GestureDetector(
                child: CircleAvatar(
                  minRadius: 40,
                  child: Text(
                    '手势竞争',
                    style: TextStyle(fontSize: 16)
                  ),
                ),
                onVerticalDragUpdate: (DragUpdateDetails details) {
                  setState(() {
                    _top += details.delta.dy;
                  });
                },
                onHorizontalDragUpdate: (DragUpdateDetails details) {
                  setState(() {
                    _left += details.delta.dx;
                  });
                },
              )
            )
          ],
        )
      )
    );
  }
}

示例效果:

每次拖动小球时,小球只会沿着一个方向移动。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱学习的程序媛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 7.1.1 基本概念
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档