Flutter中的手势系统有两个层次。第一层具有原始指针事件,其描述了穿过屏幕的指针(例如触摸、鼠标和触控笔)的位置和移动。第二层具有手势,其描述由一个或多个指针移动组成的语义动作。
指针
指针代表用户与设备屏幕交互的原始数据。有四种类型的指针事件:
- PointerDownEvent:指针已经在特定位置联系了屏幕。
- PointerMoveEvent:指针已经从屏幕上的一个位置移动到另一个位置。
- PointerUpEvent:指针已停止接触屏幕。
- PointerCancelEvent:此指针的输入不再针对此应用程序。
在指针落下,框架在您的应用程序寻找处理触摸事件的视图,以确定指针与触摸屏接触的位置处存在哪些控件。然后将指针落下事件(以及该指针的后续事件)分派到处理触摸事件的视图的最内层控件。在那里,事件会弹起控件树,并被分派到从最内层控件到树根的路径上的所有小部件。没有取消或停止指针事件进一步调度的机制。
要直接从控件层监听指针事件,可以使用Listener控件。但是,一般来说,最好使用手势(下面会介绍原因)。
手势
手势表示从多个指针事件中识别的语义动作(例如点击、拖动和缩放),甚至可能有多个指针。手势可以调度多个事件,对应于手势的生命周期(例如拖动开始、拖动更新和拖动结束):
点击
- onTapDown:可能导致点击的指针已联系到屏幕的特定位置。
- onTapUp:触发点按的指针已停止在特定位置与屏幕联系。
- onTap:发生了点击。
- onTapCancel:以前触发onTapDown的指针最终没有导致点击。
双击
- onTapDown:onDoubleTap:用户已经快速连续地在同一位置点击屏幕两次。
长按
- onLongPress:指针在相同的位置与屏幕保持的长时间接触。
垂直拖动
- onVerticalDragStart:指针已经接触到屏幕,而且可能开始垂直移动。
- onVerticalDragUpdate:与屏幕接触并垂直移动的指针沿垂直方向移动。
- onVerticalDragEnd:以前与屏幕接触并垂直移动的指针不再与屏幕接触,并且当其停止接触屏幕时以特定速度移动。
水平拖动
- onHorizontalDragStart:指针已经接触到屏幕,而且可能开始水平移动。
- onHorizontalDragUpdate:与屏幕接触并水平移动的指针沿水平方向移动。
- onHorizontalDragEnd:以前与屏幕接触并水平移动的指针不再与屏幕接触,并且当其停止接触屏幕时以特定速度移动。
要从控件层监听手势,可以使用GestureDetector控件。
如果使用质感设计控件,则其中许多控件已经响应了点击或手势。例如,IconButton和FlatButton响应点击,ListView响应滑动来触发滚动。如果没有使用这些控件,但是想要“ink splash”的点击效果,您可以使用InkWell控件。
手势消歧
在屏幕上给定的位置,可能有多个手势检测器。所有这些手势检测器在流过指针事件流时都会监听,并尝试识别特定的手势。GestureDetector控件基于哪个回调非空,决定尝试识别哪些手势。
当屏幕上给定指针有多个手势识别器时,框架通过使每个识别器加手势竞技场来决定用户打算使用哪个手势。手势竞技场使用以下规则决定哪个手势赢:
- 在任何时候,识别器可以宣布失败并离开竞技场。如果竞技场中只剩下一个识别器,则该识别器是获胜者。
- 在任何时候,识别器可以宣布胜利,这导致其获胜,所有剩余的识别器都会失败。
例如,当消除水平和垂直拖动的歧义时,当他们收到指针落下事件时,两个识别器进入竞技场。识别器观察指针移动事件。如果用户水平移动指针超过一定数量的逻辑像素,水平识别器将声明胜利,手势将被解释为水平拖动。类似地,如果用户垂直移动超过一定数量的逻辑像素,则垂直识别器将声明胜利。
当只有水平(或垂直)拖动识别器时,手势竞技场是有益的。在这种情况下,竞技场只有一个识别器,水平拖动将被立即识别,这意味着水平移动的第一个像素可以被视为拖动,用户不需要等待进一步的手势消歧。