Flutter学习(二)——触摸事件和手势
各种 Widget 进行组合之后,可以得到我们需要的画面。这肯定是不够的,我们需要与页面元素进行交互。这也就是本文所要介绍的 Pointer Event(指针事件) 和 Gesture(手势)。
这篇文章简单介绍一下指针事件和手势的使用方式。
Pointer Event
在移动设备上,Point Event 也就是我们熟悉的触摸事件。当然,Flutter 可支持的平台众多,这个 Pointer Event 的触发者可能是由手指、鼠标、触摸板等。但是,无论触发者是谁,这个指针事件的处理阶段还是被分为了四个阶段:手指按下、手指移动、手指抬起或事件取消。
- PointerDownEvent
手指按下时,Flutter 会开始执行 Hit Test 来确定当前触摸事件点击位置存在哪些组件,会按照深度优先遍历当前渲染树,对每一个渲染对象进行 Hit Test,如果命中测试通过,则该渲染对象会被添加到一个 HitTestResult 列表当中。
- PointerMoveEvent
命中测试完毕后,会遍历 HitTestResult 列表,调用每一个渲染对象的事件处理方法(handleEvent)来处理 PointerDownEvent 事件,该过程称为“事件分发”(event dispatch)。随后当手指移动时,便会分发 PointerMoveEvent 事件。
- PointerUpEvent、PointerCancelEvent
对相应的事件进行分发,分发完毕后会清空 HitTestResult 列表。
代码演示
1 | class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver { |
PointerDownEvent、 PointerMoveEvent、 PointerUpEvent 都是 PointerEvent 的子类,PointerEvent 类中包括当前指针的一些信息。这里罗列一些常用的:
- position:它是指针相对于当对于全局坐标的偏移。
- localPosition: 它是指针相对于当对于本身布局坐标的偏移。
- delta:两次指针移动事件(PointerMoveEvent)的距离。
- pressure:按压力度,如果手机屏幕支持压力传感器(如 iPhone 的 3D Touch),此属性会更有意义,如果手机不支持,则始终为 1。
- orientation:指针移动方向,是一个角度值。
忽略指针事件
使用 IgnorePointer 和 AbsorbPointer,这两个组件都能阻止子树接收指针事件。
- AbsorbPointer,会参与命中测试。AbsorbPointer 本身是可以接收指针事件的(但其子树不行)。
- IgnorePointer,本身不会参与命中测试。
Gesture
GestureDetector
GestureDetector 是一个用于手势识别的功能性组件,我们通过它可以来识别各种手势。GestureDetector 内部封装了 Listener,用以识别语义化的手势。GestureDetector 直接可以接收一个子 widget。
1 | class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver { |
- Tap
- onTapDown 指针已经在特定位置与屏幕接触
- onTapUp 指针停止在特定位置与屏幕接触
- onTap tap事件触发
- onTapCancel 先前指针触发的onTapDown不会在触发tap事件
- 双击
- onDoubleTap 用户快速连续两次在同一位置轻敲屏幕.
- 长按
- onLongPress 指针在相同位置长时间保持与屏幕接触
- 垂直拖动
- onVerticalDragStart 指针已经与屏幕接触并可能开始垂直移动
- onVerticalDragUpdate 指针与屏幕接触并已沿垂直方向移动.
- onVerticalDragEnd 先前与屏幕接触并垂直移动的指针不再与屏幕接触,并且在停止接触屏幕时以特定速度移动
- 水平拖动
- onHorizontalDragStart 指针已经接触到屏幕并可能开始水平移动
- onHorizontalDragUpdate 指针与屏幕接触并已沿水平方向移动
- onHorizontalDragEnd 先前与屏幕接触并水平移动的指针不再与屏幕接触,并在停止接触屏幕时以特定速度移动
GestureRecognizer
GestureDetector 内部是使用一个或多个 GestureRecognizer 来识别各种手势的,而 GestureRecognizer 的作用就是通过 Listener 来将原始指针事件转换为语义手势,GestureDetector 直接可以接收一个子 widget。GestureRecognizer 是一个抽象类,一种手势的识别器对应一个 GestureRecognizer 的子类,Flutter 实现了丰富的手势识别器,我们可以直接使用。
举个栗子
假设我们要给一段富文本(RichText)的不同部分分别添加点击事件处理器,但是 TextSpan 并不是一个 widget,这时我们不能用 GestureDetector,但 TextSpan 有一个 recognizer 属性,它可以接收一个 GestureRecognizer。
1 | import 'package:flutter/gestures.dart'; |
- 使用 GestureRecognizer 之前,记得导入 import ‘package:flutter/gestures.dart’;
- 使用 GestureRecognizer 后一定要调用其 dispose() 方法来释放资源(主要是取消内部的计时器)。
在实际使用时,有很多 Widget 已经对 tap 或手势做了响应。例如 FlatButton 响应 presses,ListView 响应滑动事件触发滚动。