以降のセクションでは、ドラッグ&ドロップ プロセスの主なコンセプトについて説明します。
ドラッグ&ドロップ プロセス
ドラッグ&ドロップのプロセスには、開始、継続、ドロップ、終了の 4 つのステップ(状態)があります。
- 開始
ユーザーのドラッグ操作に応じて、アプリは
startDragAndDrop()
を呼び出して、ドラッグ&ドロップ オペレーションを開始するようシステムに指示します。このメソッドの引数は次のとおりです。- ドラッグするデータ。
- ドラッグ シャドウを描画するためのコールバック
- ドラッグしたデータを説明するメタデータ
- システムは、ドラッグ シャドウを取得するためにアプリにコールバックすることで応答します。それにより、デバイスにドラッグ シャドウが表示されます。
- 次に、アクション タイプ
ACTION_DRAG_STARTED
のドラッグ イベントが、現在のレイアウト内のすべてのView
オブジェクトのドラッグ イベント リスナーに送信されます。ドラッグ イベント(発生する可能性のあるドロップ イベントを含む)を引き続き受信するには、ドラッグ イベント リスナーがtrue
を返す必要があります。これにより、リスナーがシステムに登録されます。登録されたリスナーのみが、引き続きドラッグ イベントを受信します。この時点でリスナーは、ドロップ ターゲットのView
オブジェクトの外観を変更して、ビューがドロップ イベントを受け入れることができることを示すこともできます。 - ドラッグ イベント リスナーが
false
を返す場合、システムがアクション タイプACTION_DRAG_ENDED
のドラッグ イベントを送信するまで、現在のオペレーションのドラッグ イベントを受信しません。false
を返すと、リスナーはシステムに対して、ドラッグ&ドロップ オペレーションには関係がなく、ドラッグされたデータを受け入れないことを認識します。
- ドラッグ中
- ユーザーがドラッグを続行します。ドラッグ シャドウがドロップ ターゲットの境界ボックスと交差すると、1 つ以上のドラッグ イベントがターゲットのドラッグ イベント リスナーに送信されます。リスナーは、このイベントに反応して、ドロップ ターゲット
View
の外観を変更することがあります。たとえば、ドラッグ シャドウがドロップ ターゲットの境界ボックスに入ったことを示すイベント(アクション タイプACTION_DRAG_ENTERED
)の場合、リスナーはView
をハイライト表示して反応できます。 - ドロップ
- ユーザーが、ドロップ ターゲットの境界ボックス内でドラッグ シャドウを解放します。システムは、ドロップ ターゲットのリスナーに、アクション タイプ
ACTION_DROP
のドラッグ イベントを送信します。ドラッグ イベント オブジェクトには、オペレーションを開始するstartDragAndDrop()
の呼び出しでシステムに渡されるデータが含まれています。リスナーは、ドロップされたデータを正常に処理した場合、システムにブール値true
を返すことが想定されます。: このステップは、ユーザーがドラッグ シャドウを、View
の境界ボックス内にドロップした場合にのみ発生します。このシャドウのリスナーは、ドラッグ イベント(ドロップ ターゲット)を受信するよう登録されています。それ以外の状況でユーザーがドラッグ シャドウを解放しても、ACTION_DROP
ドラッグ イベントは送信されません。 - 終了済み
ユーザーがドラッグ シャドウを解放した後、
アクション タイプ
ACTION_DROP
のドラッグ イベントを送信すると、必要に応じて、ドラッグ&ドロップ オペレーションが終了したことを示すアクション タイプACTION_DRAG_ENDED
のドラッグ イベントが送信されます。これは、ユーザーがドラッグ シャドウを解放した場所に関係なく行われます。このイベントは、ACTION_DROP
イベントも受信する場合でも、ドラッグ イベントを受信するように登録されているすべてのリスナーに送信されます。
各ステップの詳細については、ドラッグ&ドロップ オペレーションのセクションをご覧ください。
ドラッグ イベント
システムはドラッグ イベントを DragEvent
オブジェクトの形式で送信します。このオブジェクトには、ドラッグ&ドロップ プロセス内の状況を表すアクション タイプが含まれています。アクション タイプに応じて、オブジェクトに他のデータを含めることもできます。
ドラッグ イベント リスナーは DragEvent
オブジェクトを受け取ります。リスナーでアクション タイプを取得するためには、DragEvent.getAction()
を呼び出します。DragEvent
クラスの定数によって定義される有効な値は 6 つあります(表 1 を参照)。
アクション タイプ | 意味 |
---|---|
ACTION_DRAG_STARTED |
アプリは startDragAndDrop() を呼び出し、ドラッグ シャドウを取得します。リスナーがこのオペレーションのドラッグ イベントを引き続き受信するには、リスナーからシステムにブール値 true を返す必要があります。
|
ACTION_DRAG_ENTERED |
ドラッグ シャドウが、ドラッグ イベント リスナーの View の境界ボックスに入ります。これは、ドラッグ シャドウが境界ボックスに入ったときにリスナーが最初に受信するイベント アクション タイプです。
|
ACTION_DRAG_LOCATION |
ACTION_DRAG_ENTERED イベントの後、ドラッグ シャドウは、ドラッグ イベント リスナーの View の境界ボックス内に引き続き表示されます。 |
ACTION_DRAG_EXITED |
ACTION_DRAG_ENTERED と少なくとも 1 つの ACTION_DRAG_LOCATION イベントの後に、ドラッグ シャドウは、ドラッグ イベント リスナーの View の境界ボックスの外に移動します。 |
ACTION_DROP |
ドラッグ シャドウは、ドラッグ イベント リスナーの View 上で解放されます。このアクション タイプが View オブジェクトのリスナーに送信されるのは、ACTION_DRAG_STARTED ドラッグ イベントに対してリスナーがブール値 true を返す場合のみです。リスナーが登録されていない View 上でユーザーがドラッグ シャドウを解放した場合、または現在のレイアウトに含まれない要素上でドラッグ シャドウを解放した場合、このアクション タイプは送信されません。
ドロップを正常に処理すると、リスナーからブール値 |
ACTION_DRAG_ENDED |
ドラッグ&ドロップ操作を終了します。このアクション タイプの前に ACTION_DROP イベントが発生するとは限りません。システムから ACTION_DROP が送信された場合、ACTION_DRAG_ENDED アクション タイプを受信しても、ドロップが成功したとは限りません。ACTION_DROP へのレスポンスとして返される値を取得するには、表 2 に示すように、リスナーが getResult() を呼び出す必要があります。ACTION_DROP イベントが送信されない場合、getResult() は false を返します。 |
DragEvent
オブジェクトには、startDragAndDrop()
の呼び出しでアプリがシステムに提供するデータとメタデータも含まれています。表 2 に示すように、一部のデータは特定のアクション タイプに対してのみ有効です。イベントと関連データの詳細については、ドラッグ&ドロップ オペレーションをご覧ください。
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
クラスには、以下の 2 つのコンストラクタがあります。
View.DragShadowBuilder(View)
このコンストラクタでは、アプリのあらゆる
View
オブジェクトを使用できます。このコンストラクタによりView
オブジェクトがView.DragShadowBuilder
オブジェクトに格納されるため、コールバックがアクセスしてドラッグ シャドウを作成できます。ビューは、ユーザーがドラッグ オペレーションを開始するときに選択するView
である必要はありません。このコンストラクタを使用すれば、
View.DragShadowBuilder
を拡張したり、そのメソッドをオーバーライドしたりする必要がなくなります。デフォルトでは、引数として渡すView
と同じ外観のドラッグ シャドウが、ユーザーが画面にタップした位置の中央に表示されます。View.DragShadowBuilder()
このコンストラクタを使用する場合、
View.DragShadowBuilder
オブジェクトではView
オブジェクトを使用できません。このフィールドはnull
に設定されます。View.DragShadowBuilder
を拡張してそのメソッドをオーバーライドする必要があります。そうしないと、ドラッグ シャドウが非表示になります。システムからエラーはスローされません。
View.DragShadowBuilder
クラスには、ドラッグ シャドウを一緒に作成する 2 つのメソッドがあります。
onProvideShadowMetrics()
startDragAndDrop()
を呼び出すとすぐに、システムによってこのメソッドが呼び出されます。このメソッドを使用して、ドラッグ シャドウのサイズやタッチポイントをシステムに送信します。このメソッドには、次の 2 つのパラメータがあります。outShadowSize
:Point
オブジェクト。ドラッグ シャドウの幅はx
、高さはy
で指定します。outShadowTouchPoint
:Point
オブジェクト。タッチポイントとは、ドラッグ シャドウ内の位置で、ユーザーの指の下に位置する必要があります。X の位置をx
、Y の位置をy
に指定します。onDrawShadow()
onProvideShadowMetrics()
呼び出しの直後に、ドラッグ シャドウを作成するonDrawShadow()
が呼び出されます。このメソッドの引数はCanvas
オブジェクトです。このオブジェクトは、onProvideShadowMetrics()
で指定したパラメータからシステムが作成します。このメソッドは、指定されたCanvas
にドラッグ シャドウを描画します。
パフォーマンスを向上させるには、ドラッグ シャドウのサイズを小さくします。単一のアイテムには、アイコンを使用することをおすすめします。複数のアイテムを選択する場合は、画面全体に広がるフル画像ではなく、積み重ねたアイコンを使用することをおすすめします。
ドラッグ イベント リスナーとコールバック メソッド
View
は、View.OnDragListener
を実装するドラッグ イベント リスナー、またはビューの onDragEvent()
コールバック メソッドで、ドラッグ イベントを受け取ります。システムは、このメソッドまたはリスナーを呼び出すときに DragEvent
引数を指定します。
ほとんどの場合、コールバック メソッドよりもリスナーを使用することをおすすめします。UI を設計する場合、通常は View
クラスをサブクラス化しませんが、コールバック メソッドを使う場合は、メソッドをオーバーライドするためにサブクラスを作成する必要があります。これに対してリスナークラスは、1 つ実装すれば、そのリスナークラスを異なる複数の View
オブジェクトで使用できます。匿名のインライン クラスまたはラムダ式として実装することもできます。View
オブジェクトのリスナーを設定するには、setOnDragListener()
を呼び出します。
別の方法として、メソッドをオーバーライドせずに onDragEvent()
のデフォルト実装を変更することもできます。ビューに OnReceiveContentListener
を設定します。詳細については、setOnReceiveContentListener()
をご覧ください。すると、onDragEvent()
メソッドはデフォルトで次の処理を行います。
startDragAndDrop()
の呼び出しに応答して true を返します。ドラッグ&ドロップ データがビューにドロップされた場合は、
performReceiveContent()
が呼び出されます。データはContentInfo
オブジェクトとしてメソッドに渡されます。このメソッドはOnReceiveContentListener
を呼び出します。ドラッグ&ドロップ データがビューにドロップされ、
OnReceiveContentListener
がいずれかのコンテンツを使用すると、true が返されます。
アプリのデータを処理するように OnReceiveContentListener
を定義します。API レベル 24 までの下位互換性を確保するには、Jetpack バージョンの OnReceiveContentListener
を使用します。
View
オブジェクトには、ドラッグ イベント リスナーとコールバック メソッドを使用できます。この場合、システムが最初にリスナーを呼び出します。リスナーが false
を返さない限り、システムはコールバック メソッドを呼び出しません。
onDragEvent()
メソッドと View.OnDragListener
の組み合わせは、タッチイベントで使用される onTouchEvent()
と View.OnTouchListener
の組み合わせに似ています。