點選、拖動和其他手勢
這個章節將會講解如何監聽和響應 Flutter 的手勢操作 gestures。典型的手勢操作包括點選、拖動和縮放。
Flutter 中的手勢有兩個不同的層次:第一層是原始的指針指向事件,描述了螢幕上由觸控板、滑鼠、指示筆等觸發的指標的位置和移動。第二層包含 gestures,描述了由上述一個或多個指標移動組成的具有特殊語義的操作。
指標
Pointer 代表的是人機介面互動的原始資料。一共有四種指標事件:
PointerDownEvent
指標在特定位置與螢幕接觸
PointerMoveEvent
指標從螢幕的一個位置移動到另外一個位置
PointerUpEvent
指標與螢幕停止接觸
PointerCancelEvent
指標的輸入已經不再指向此應用
在指標下落事件中,框架做了一個 hit test 的操作確定與螢幕發生接觸的位置上有哪些元件以及分發給最內部的元件去響應。事件會沿著元件樹從這個最內部的元件向元件樹的根部冒泡分發。並且不存在用於取消或停止指標事件進行進一步分發的機制。
使用 Listener
可以在元件層直接監聽指標事件。然而,一般情況下,請考慮使用下面的 gestures 替代。
手勢
Gesture 代表的是語義操作(比如點選、拖動、縮放)。通常由一系列單獨的指標事件組成,甚至是一系列單獨的指標組成。 Gesture 可以分發多種事件,對應著手勢的生命週期(比如開始拖動、拖動更新、結束拖動)。
點選
onTapDown
指標在發生接觸的螢幕的特定位置可能引發點選事件。
onTapUp
指標使在螢幕的特定位置觸發的點選事件停止。
onTap
點選事件已經發生。
onTapCancel
指標已經觸發了 onTapDown
,但是最終不會形成一個點選事件。
雙擊
onDoubleTap
使用者在螢幕的相同位置上快速點選了兩次。
長按
onLongPress
指標在螢幕的相同位置上保持接觸持續一長段時間。
縱向拖動
onVerticalDragStart
指標和螢幕產生接觸並可能開始縱向移動。
onVerticalDragUpdate
指標和螢幕產生接觸,在縱向上發生移動並保持移動。
onVerticalDragEnd
指標先前和螢幕產生了接觸,以特定速度縱向移動,並且此後不會在螢幕接觸上發生縱向移動。
橫向拖動
onHorizontalDragStart
指標和螢幕產生接觸並可能開始橫向移動。
onHorizontalDragUpdate
指標和螢幕產生接觸,在橫向上發生移動並保持移動。
onHorizontalDragEnd
指標先前和螢幕產生了接觸,以特定速度橫向移動,並且此後不會在螢幕接觸上發生橫向移動。
移動
onPanStart
指標和螢幕產生接觸並可能開始橫向移動或者縱向移動。如果設定了 onHorizontalDragStart
或者 onVerticalDragStart
,該回調方法會引發崩潰。
onPanUpdate
指標和螢幕產生接觸,在橫向或者縱向上發生移動並保持移動。如果設定了 onHorizontalDragUpdate
或者 onVerticalDragUpdate
,該回調方法會引發崩潰。
onPanEnd
指標先前和螢幕產生了接觸,並且以特定速度移動,此後不再在螢幕接觸上發生移動。如果設定了 onHorizontalDragEnd
或者 onVerticalDragEnd
,該回調方法會引發崩潰。
為 widgets 新增手勢檢測
從元件層監聽手勢,需要用到 GestureDetector
。
如果使用 Material 風格的元件,其中的許多元件都能夠支援響應點選或者手勢事件。比如 IconButton
和 TextButton
響應了按壓事件(點選事件),
ListView
響應了滾動事件。如果使用了上述元件,你也可以使用 InkWell
來實現點選的“水波紋”效果。
手勢消歧處理
在螢幕的指定位置上,可能有多個手勢捕捉器。所有的手勢捕捉器監聽了指標輸入流事件並判斷出特定手勢。
GestureDetector
widget
能夠基於手勢的回呼(Callback)是否非空決定是否應該嘗試去識別該手勢。
當螢幕上的指定指標有多個手勢識別器時,框架會透過給每個手勢識別器加入 gesture arena 來處理手勢消歧。 gesture arena,也稱作手勢競技場,會利用下述規則確定哪個手勢在競爭中勝出:
-
在任何時候,識別器都可以宣告失敗並離開競技場。如果競技場中只有一個識別器,那麼這個識別器就是勝者。
-
在任何時候,任何識別器都可以宣告勝利,這將導致這個識別器勝出,其他識別器失敗。
比如,當縱向拖動和橫向拖動需要處理消歧,當指標下落事件發生時,縱向和橫向識別器都會進入競技場,觀測指標移動事件。如果使用者在橫向上移動超過了特定畫素,橫向識別器會宣告勝利,手勢也會被當作橫向拖動處理。同樣的,如果使用者在縱向上移動超過了特定的畫素,縱向識別器會宣告勝利。
手勢競技場在僅有一個橫向(或者縱向)拖動識別器的時候是非常高效的。在此範例中,競技場中只有一個橫向識別器,橫向拖動就能夠被立即識別到,這意味著橫向移動從第一個畫素開始就能夠被立即處理成橫向拖動手勢,而並不需要等待進一步的手勢消歧處理。