以降のセクションでは、ドラッグ&ドロップ プロセスの重要なコンセプトについて説明します。
ドラッグ&ドロップ プロセス
ドラッグ&ドロップ プロセスには、開始、続行、ドロップ、終了の 4 つのステップまたは状態があります。
- 開始
ユーザーのドラッグ ジェスチャーに応じて、アプリは
startDragAndDrop()を呼び出し、ドラッグ&ドロップ オペレーションを開始するようシステムに指示します。このメソッドの引数は次のとおりです。- ドラッグするデータ。
- ドラッグ シャドウを描画するためのコールバック
- ドラッグしたデータを説明するメタデータ
- システムは、アプリにコールバックしてドラッグ シャドウを取得します。これにより、デバイスにドラッグ シャドウが表示されます。
- 次に、システムから現在のレイアウト上にあるすべての
Viewオブジェクトのドラッグ イベント リスナーに対して、アクション タイプACTION_DRAG_STARTEDのドラッグ イベントが送信されます。ドラッグ イベント リスナーでは、今後発生する可能性のあるドロップ イベントなどのドラッグ イベントを受信し続けるには、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() を呼び出します。
取り得る値は 6 つあり、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 と少なくとも 1 つの
ACTION_DRAG_LOCATION イベントの後に、ドラッグ シャドウが
ドラッグ イベント リスナーの
View の境界ボックスの外に移動します。
|
ACTION_DROP |
ドラッグ シャドウが、ドラッグ イベント リスナーの
View上で解放されます。このアクション タイプが View
オブジェクトのリスナーに送信されるのは、そのリスナーがブール値
true を
ACTION_DRAG_STARTED ドラッグ イベントに対する応答として返した場合のみです。ユーザーがドラッグ シャドウを解放した場所が、リスナーが登録されていない View 上であるか、現在のレイアウトに含まれていない場所である場合は、このアクション タイプは送信されません。
ドロップを正常に処理した場合は、リスナーからブール値 |
ACTION_DRAG_ENDED |
システムがドラッグ&ドロップ オペレーションを終了します。このアクション タイプ
の前に ACTION_DROP イベントが発生するとは限りません。システムから ACTION_DROP が送信されていた場合、ACTION_DRAG_ENDED アクション タイプを受信したからといって、ドロップが正常に処理されたことにはなりません。リスナーは、ACTION_DROP への応答で返された値を取得するには、表 2 に示すように、getResult() を呼び出す必要があります。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 が返されます。
ドラッグ シャドウ
ドラッグ&ドロップ オペレーション中、システムはユーザーがドラッグする画像を表示します。データを移動する場合は、この画像はドラッグ中のデータを表します。その他の操作の場合は、そのドラッグ オペレーションをなんらかのかたちで表す画像になります。
この画像を、ドラッグ シャドウと呼びます。 これは、
a
View.DragShadowBuilder
オブジェクト用に宣言したメソッドで作成します。startDragAndDrop() を使用してドラッグ&ドロップ オペレーションを開始するときに、ビルダーをシステムに渡します。システムでは、startDragAndDrop() に対する応答の一環として、View.DragShadowBuilder で定義したコールバック メソッドが呼び出され、ドラッグ シャドウが取得されます。
View.DragShadowBuilder クラスには、以下の 2 つのコンストラクタがあります。
View.DragShadowBuilder(View)このコンストラクタでは、アプリのあらゆる
Viewオブジェクトを使用できます。このコンストラクタによりViewオブジェクトがView.DragShadowBuilderオブジェクトに格納されるため、コールバックが アクセスしてドラッグ シャドウを作成できます。ビューは、ユーザーがドラッグ操作を開始するために選択するViewである必要はありません。このコンストラクタを使用すれば、
View.DragShadowBuilderを拡張したり、そのメソッドをオーバーライドしたりする必要がなくなります。デフォルトでは、ドラッグ シャドウの外観は引数として渡したViewと同じになり、画面上でユーザーがタップしている位置を中心として表示されます。View.DragShadowBuilder()このコンストラクタでは、
Viewオブジェクト内でView.DragShadowBuilderオブジェクトを指定しません。フィールドはnullに設定されます。View.DragShadowBuilderの拡張や、そのメソッドのオーバーライドを行わない限り、ドラッグ シャドウは表示されません。システムはエラーをスローしません。
View.DragShadowBuilder クラスには、ドラッグ シャドウを一緒に作成するメソッドが 2 つあります。
onProvideShadowMetrics()startDragAndDrop()を呼び出すとすぐに、システムによってこのメソッドが呼び出されます。このメソッドを使用して、ドラッグ シャドウのサイズやタッチポイントをシステムに送信します。このメソッドには、次の 2 つのパラメータがあります。outShadowSize:Pointオブジェクト。ドラッグ シャドウの幅をx、高さをyに指定します。outShadowTouchPoint:Pointオブジェクト。タッチポイントとは、ドラッグ中にユーザーの指の下にくるドラッグ シャドウ内の位置です。 その X 座標はx、Y 座標はyに指定します。onDrawShadow()onProvideShadowMetrics()呼び出しの直後に、ドラッグ シャドウを作成するonDrawShadow()が呼び出されます。このメソッドには、システムがonProvideShadowMetrics()で指定したパラメータから構築するCanvasオブジェクトという 1 つの引数があります。このメソッドを使って、用意されたCanvas内にドラッグ シャドウを描画します。
パフォーマンスを向上させるには、ドラッグ シャドウのサイズを小さくします。単一のアイテムの場合は、アイコンを使用することをおすすめします。複数のアイテムを選択する場合は、画面全体に画像を広げるのではなく、アイコンを重ねて使用することをおすすめします。
ドラッグ イベント リスナーとコールバック メソッド
A 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 の組み合わせに似ています。