點選、拖拽及其他手勢
Flutter 中點選和拖拽等手勢的工作原理。
本文件介紹瞭如何在 Flutter 中監聽並響應手勢。常見手勢包括點選、拖拽和縮放。
Flutter 的手勢系統分為兩個獨立的層級。第一層是原始指標事件,用於描述指標(例如觸控、滑鼠和觸控筆)在螢幕上的位置和移動。第二層是手勢,用於描述由一個或多個指標移動組成的語義動作。
指標 (Pointers)
#指標代表使用者與裝置螢幕互動的原始資料。共有四種類型的指標事件:
-
PointerDownEvent 指標已在特定位置接觸螢幕。
-
PointerMoveEvent 指標已從螢幕上的一個位置移動到另一個位置。
-
PointerUpEvent 指標已停止與螢幕接觸。
-
PointerCancelEvent 來自該指標的輸入不再指向此應用。
當指標按下時,框架會對應用進行命中測試 (hit test),以確定指標接觸螢幕的位置存在哪些元件。指標按下事件(以及該指標隨後的事件)將分發給命中測試找到的最內層元件。隨後,事件會沿元件樹向上冒泡,並分發給從最內層元件到樹根路徑上的所有元件。目前沒有機制可以取消或停止指標事件的後續分發。
若要直接從元件層監聽指標事件,請使用 Listener 元件。但通常建議優先使用下文討論的“手勢”。
手勢
#手勢代表語義動作(例如點選、拖拽和縮放),這些動作透過多個獨立的指標事件識別,甚至可能涉及多個獨立的指標。手勢可以分發多個事件,對應於手勢的生命週期(例如,拖拽開始、拖拽更新和拖拽結束)。
點選
onTapDown-
可能導致點選的指標已在特定位置接觸螢幕。
onTapUp-
觸發點選的指標已在特定位置停止接觸螢幕。
onTap-
之前觸發
onTapDown的指標也觸發了onTapUp,最終導致了一次點選。 onTapCancel-
之前觸發
onTapDown的指標最終並未導致點選。
雙擊
onDoubleTap-
使用者在同一位置短時間內連續點選了兩次螢幕。
長按
onLongPress-
指標在同一位置長時間保持與螢幕接觸。
垂直拖動
onVerticalDragStart-
指標已接觸螢幕並可能開始垂直移動。
onVerticalDragUpdate-
一個正在與螢幕接觸並進行垂直移動的指標,已經在垂直方向上發生了位移。
onVerticalDragEnd-
之前與螢幕接觸並垂直移動的指標不再與螢幕接觸,並且在停止接觸螢幕時以特定速度移動。
水平拖動
onHorizontalDragStart-
指標已接觸螢幕並可能開始水平移動。
onHorizontalDragUpdate-
一個正在與螢幕接觸並進行水平移動的指標,已經在水平方向上發生了位移。
onHorizontalDragEnd-
一個之前正在與螢幕接觸並進行水平移動的指標,現在已脫離螢幕,並在停止接觸時保持著特定的速度。
平移 (Pan)
onPanStart-
指標已接觸螢幕,且可能開始進行水平或垂直移動。如果設定了
onHorizontalDragStart或onVerticalDragStart,此回撥會報錯。 onPanUpdate-
一個正在與螢幕接觸並沿垂直或水平方向移動的指標。如果設定了
onHorizontalDragUpdate或onVerticalDragUpdate,此回撥會報錯。 onPanEnd-
一個之前與螢幕接觸的指標現已脫離螢幕,並在停止接觸時保持著特定的速度。如果設定了
onHorizontalDragEnd或onVerticalDragEnd,此回撥會報錯。
為元件新增手勢檢測
#若要從元件層監聽手勢,請使用 GestureDetector。
如果您使用的是 Material 元件,其中許多元件已經能夠響應點選或手勢。例如,IconButton 和 TextButton 響應按下(點選)操作,而 ListView 透過滑動響應來觸發滾動。如果您不使用這些元件,但希望在點選時出現“墨水飛濺 (ink splash)”效果,可以使用 InkWell。
手勢消歧 (Gesture disambiguation)
#在螢幕上的特定位置,可能存在多個手勢檢測器。例如:
ListTile有一個響應整個ListTile的點選識別器,而在尾部的圖示按鈕周圍還有一個巢狀的識別器。尾部圖示的螢幕矩形區域現在被兩個手勢識別器覆蓋,如果發生了點選,它們需要協商由誰來處理該手勢。- 單個
GestureDetector覆蓋的螢幕區域配置為處理多種手勢,例如長按和點選。當用戶觸控式螢幕幕該區域時,點選識別器必須與長按識別器進行協商。根據指標後續的操作,其中一個識別器將接收該手勢;如果使用者執行的操作既不是點選也不是長按,則兩者都不會接收該手勢。
所有這些手勢檢測器都會監聽流經的指標事件流,並嘗試識別特定手勢。GestureDetector 元件會根據其哪些回撥函式非空(non-null),來決定嘗試識別哪些手勢。
當螢幕上某個指標對應多個手勢識別器時,框架會透過讓每個識別器加入手勢競技場 (gesture arena) 來確定使用者意圖,從而消除歧義。手勢競技場使用以下規則確定勝出者:
-
在任何時候,識別器都可以自我淘汰並離開競技場。如果競技場中只剩下一個識別器,該識別器勝出。
-
在任何時候,識別器都可以宣佈自己勝出,從而導致所有剩餘的識別器失敗。
例如,在區分水平拖拽和垂直拖拽時,兩個識別器在接收到指標按下事件時都會進入競技場。識別器會觀察指標移動事件。如果使用者在水平方向上移動指標超過一定數量的邏輯畫素,水平識別器就會宣佈勝出,手勢被解釋為水平拖拽。同樣,如果使用者在垂直方向上移動超過一定數量的邏輯畫素,垂直識別器就會宣佈自己勝出。
當只有水平(或垂直)拖拽識別器時,手勢競技場也非常有用。在這種情況下,競技場中只有一個識別器,水平拖拽會立即被識別,這意味著水平移動的第一畫素就可以被視為拖拽,使用者無需等待進一步的手勢消歧。