核心概念

以下各節說明拖曳程序的幾個重要概念。

拖曳程序

拖曳程序有四個步驟或狀態:已開始、執行、捨棄和結束。

已開始

為回應使用者的拖曳手勢,應用程式會呼叫 startDragAndDrop() 以指示系統開始拖曳作業。方法的引數提供以下內容:

  • 要拖曳的資料。
  • 用於繪製拖曳陰影的回呼
  • 描述拖曳資料的中繼資料
  • 系統回應時,系統會呼叫應用程式以取得拖曳陰影。然後在裝置上顯示拖曳陰影。
  • 接著,系統會將動作類型為 ACTION_DRAG_STARTED 的拖曳事件傳送至目前版面配置中所有 View 物件的拖曳事件監聽器。如要繼續接收拖曳事件 (包括可能的放置事件),拖曳事件監聽器必須回傳 true。此指令會向系統註冊事件監聽器。只有已註冊的事件監聽器會繼續接收拖曳事件。此時,事件監聽器也可以變更放置目標 View 物件的外觀,以表示檢視畫面能夠接受放置事件。
  • 如果拖曳事件監聽器回傳 false,將不會收到目前作業的拖曳事件,直到系統傳送動作類型為 ACTION_DRAG_ENDED 的拖曳事件。事件監聽器會藉由回傳 false,向系統表示不想參與拖曳作業,且不想接受拖曳的資料。
繼續
使用者繼續拖曳。當拖曳陰影與放置目標的定界框交會時,系統會傳送一或多個拖曳事件至目標的拖曳事件監聽器。事件監聽器可能會依據事件變更放置目標 View 的外觀。舉例來說,如果事件指出拖曳陰影進入放置目標的定界框 (動作類型 ACTION_DRAG_ENTERED),事件監聽器就會醒目顯示 View
已拖曳
使用者在放置目標的定界框內放開拖曳陰影。系統會向放置目標的事件監聽器傳送動作類型為 ACTION_DROP 的拖曳事件。拖曳事件物件包含在開始作業的 startDragAndDrop() 呼叫中傳遞給系統的資料。如果事件監聽器成功處理放置的資料,事件監聽器應將布林值 true 傳回系統。:只有在使用者將拖曳陰影拖曳到已經過註冊而得以接收拖曳事件的 View (放置目標) 定界框中時,系統才會執行這個步驟。如果使用者在任何其他情況下放開拖曳陰影,系統就不會傳送 ACTION_DROP 拖曳事件。
已結束

使用者放開拖曳陰影,且系統傳送

視需要傳送動作類型為 ACTION_DROP 的拖曳事件,系統會傳送動作類型為 ACTION_DRAG_ENDED 的拖曳事件,表示拖曳作業已結束。無論使用者放開拖曳陰影的位置為何。即使事件監聽器收到 ACTION_DROP 事件,系統仍會將事件傳送給每個註冊接收拖曳事件的事件監聽器。

如要進一步瞭解這些步驟,請參閱「拖曳作業」一節。

拖曳事件

系統會以 DragEvent 物件的形式傳送拖曳事件,其中包含描述拖曳過程的動作類型。視動作類型而定,物件中也可能包含其他資料。

拖曳事件監聽器會接收 DragEvent 物件。為了取得動作類型,事件監聽器會呼叫 DragEvent.getAction()DragEvent 類別中的常數定義了六個可能的值,如表 1 所述:

表 1. DragEvent 動作類型

動作類型 意義
ACTION_DRAG_STARTED 應用程式會呼叫 startDragAndDrop() 並取得拖曳陰影。如果事件監聽器要繼續接收此作業的拖曳事件,則必須將布林值 true 回傳系統。
ACTION_DRAG_ENTERED 拖曳陰影會進入拖曳事件監聽器 View 的定界框。這是拖曳陰影進入定界框時,事件監聽器收到的第一個事件動作類型。
ACTION_DRAG_LOCATION ACTION_DRAG_ENTERED 事件之後,拖曳陰影仍位於拖曳事件監聽器 View 的定界框內。
ACTION_DRAG_EXITED ACTION_DRAG_ENTERED 和至少一個 ACTION_DRAG_LOCATION 事件之後,拖曳陰影會移動至拖曳事件監聽器 View 的定界框外。
ACTION_DROP 拖曳陰影會在拖曳事件監聽器的 View 上放開。只有在事件監聽器回傳布林值 true 以回應 ACTION_DRAG_STARTED 拖曳事件時,系統才會將動作類型傳送至 View 物件的事件監聽器。如果使用者在未註冊事件監聽器的 View 上放開拖曳陰影,或在不屬於目前版面配置的任一處放開,就不會傳送此動作類型。

如果成功處理放置,事件監聽器會傳回布林值 true。否則,則必須傳回 false

ACTION_DRAG_ENDED 系統即將結束拖曳作業。動作類型不一定要在 ACTION_DROP 事件前面。如果系統傳送 ACTION_DROP,即使收到 ACTION_DRAG_ENDED 動作類型,也不代表拖曳成功。事件監聽器必須呼叫 getResult() (如表 2 所示) 以取得回應 ACTION_DROP 時回傳的值。如未傳送 ACTION_DROP 事件,則 getResult() 會回傳 false

DragEvent 物件也會包含應用程式在呼叫 startDragAndDrop() 時提供給系統的資料和中繼資料。有些資料僅適用於特定動作類型,如表 2 所列。如要進一步瞭解事件及相關資料,請參閱「拖曳作業」一節。

表 2. 各動作類型的有效 DragEvent 資料

getAction()
價值
getClipDescription()
價值
getLocalState()
價值
getX()
價值
getY()
價值
getClipData()
價值
getResult()
價值
ACTION_DRAG_STARTED ✓ ✓        
ACTION_DRAG_ENTERED ✓ ✓        
ACTION_DRAG_LOCATION ✓ ✓ ✓ ✓    
ACTION_DRAG_EXITED ✓ ✓        
ACTION_DROP ✓ ✓ ✓ ✓ ✓  
ACTION_DRAG_ENDED   ✓       ✓

DragEvent 方法 getAction()describeContents()writeToParcel()toString() 一律會傳回有效資料。

如果方法未包含特定動作類型的有效資料,系統會根據其結果類型傳回 null 或 0。

拖曳陰影

在拖曳作業期間,系統會顯示使用者拖曳的圖片。若是移動資料,此圖片則代表拖曳的資料。若是其他作業,圖片則代表拖曳作業的某些部分。

此圖片稱為「拖曳陰影」。請使用您為 View.DragShadowBuilder 物件宣告的方法建立。使用 startDragAndDrop() 啟動拖曳作業時,會將建構工具傳遞至系統。為回應 startDragAndDrop(),系統會叫用您在 View.DragShadowBuilder 中定義的回呼方法,藉此取得拖曳陰影。

View.DragShadowBuilder 類別有兩個建構函式:

View.DragShadowBuilder(View)

此建構函式接受應用程式的任何 View 物件。建構函式會將 View 物件儲存在 View.DragShadowBuilder 物件中,因此回呼可以存取該物件來建構拖曳陰影。檢視畫面不一定要是使用者為了開始作業而選取的 View

使用此建構函式時,您不必擴充 View.DragShadowBuilder 或覆寫其方法。根據預設,拖曳陰影的外觀會與做為引數傳遞的 View 相同,並位於使用者輕觸螢幕的位置下方。

View.DragShadowBuilder()

如果使用此建構函式,View.DragShadowBuilder 物件中就不會有 View 物件。這個欄位已設為 null。您必須擴充 View.DragShadowBuilder 並覆寫其方法,否則會發生看不到的拖曳陰影。系統不會擲回錯誤。

View.DragShadowBuilder 類別有兩個方法共同建立拖曳陰影:

onProvideShadowMetrics()

當您呼叫 startDragAndDrop() 後,系統會立即呼叫此方法。此方法會將拖曳陰影的維度和觸控點傳送到系統。此方法有兩個參數:

outShadowSizePoint 物件。拖曳陰影寬度為 x,高度為 y

outShadowTouchPointPoint 物件。觸控點是指拖曳陰影內的位置,拖曳時必須位在使用者手指下方。其「X」位置位於 xY 位置則為 y

onDrawShadow()

呼叫 onProvideShadowMetrics() 後,系統會立即呼叫 onDrawShadow() 來建立拖曳陰影。此方法有一個引數,即 Canvas 物件,系統會根據您在 onProvideShadowMetrics() 中提供的參數建構。此方法會在提供的 Canvas 上繪製拖曳陰影。

為提升效能,請盡量使用較小的拖曳陰影。如果是單一項目,建議您使用圖示。如果需要選擇多個項目,建議您使用堆疊中的圖示,而不要將整個圖片蓋滿螢幕畫面。

拖曳事件監聽器和回呼方法

View 收到的拖曳事件可能包含實作 View.OnDragListener 的拖曳事件監聽器,或利用檢視畫面的 onDragEvent() 回呼方法。當系統呼叫方法或事件監聽器時,會提供 DragEvent 引數。

在大部分情況下,使用監聽器時最好使用回呼方法。設計 UI 時,您通常不會將 View 類別設為子類別,不過如果您使用回呼方法,則必須建立子類別來覆寫此方法。相較之下,您可以導入一個事件監聽器類別,然後搭配多個不同的 View 物件使用。您也可以將此實作為匿名內嵌類別或 lambda 運算式。如要設定 View 物件的事件監聽器,請呼叫 setOnDragListener()

或者,您也可以在不覆寫該方法的情況下,修改 onDragEvent() 的預設實作方式。在檢視區塊上設定 OnReceiveContentListener;詳情請參閱 setOnReceiveContentListener()。根據預設,onDragEvent() 方法會執行以下操作:

  • 回傳 true 值以回應對 startDragAndDrop() 的呼叫。
  • 如果拖曳資料在檢視畫面上放置,系統會呼叫 performReceiveContent()。資料會以 ContentInfo 物件的形式傳遞至方法。此方法會叫用 OnReceiveContentListener

  • 如果拖放資料在檢視畫面上放置,且 OnReceiveContentListener 會使用任何內容,則會回傳 true。

定義 OnReceiveContentListener 以專門處理應用程式的資料。為了回溯相容到 API 級別 24,請使用 OnReceiveContentListener 的 Jetpack 版本。

您可以為 View 物件設定拖曳事件監聽器和回呼方法。在此情況下,系統會先呼叫事件監聽器。除非監聽器回傳 false,否則系統不會呼叫回呼方法。

onDragEvent() 方法和 View.OnDragListener 的組合類似觸控事件使用的 onTouchEvent()View.OnTouchListener