シンプルなドラッグ&ドロップ用の DropHelper

DropHelper クラスを使用すると、ドラッグ&ドロップ機能の実装が簡単になります。Jetpack の DragAndDrop ライブラリのメンバーである DropHelper は、API レベル 24 までの下位互換性を提供しています。

DropHelper によって、ドロップ ターゲットの指定、ドロップ ターゲットのハイライト表示のカスタマイズ、ドロップされたデータの処理方法の定義が可能です。

ドラッグのソースを設定

まず、ドラッグ ソースビューと OnDragStartListener を使用して DragStartHelper を作成します。

OnDragStartListener で、onDragStart() メソッドをオーバーライドします。移動するデータの ClipData オブジェクトと ClipData.Item オブジェクトを作成します。ClipData の一部として、ClipData 内の ClipDescription オブジェクトに格納されているメタデータを指定します。データの移動を表していないドラッグ&ドロップ オペレーションの場合は、実際のオブジェクトの代わりに null を使用することをおすすめします。

Kotlin

DragStartHelper(draggableView)
    { view: View, _: DragStartHelper ->
        val item = ClipData.Item(view.tag as? CharSequence)
        val dragData = ClipData(
            view.tag as? CharSequence,
            arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN),
            item
        )
        view.startDragAndDrop(
            dragData,
            View.DragShadowBuilder(view),
            null,
            0
        )
    }.attach()

Java

new DragStartHelper(draggableView, new DragStartHelper.OnDragStartListener() {
    @Override
    public void onDragStart(View view, DragStartHelper helper) {
        CharSequence tag = (CharSequence) view.getTag();
        ClipData.Item item = new ClipData.Item(tag);
        ClipData dragData = new ClipData(
          tag, new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}, item);
        view.startDragAndDrop(
          dragData, new View.DragShadowBuilder(view), null, 0);
    }
});

ドロップ ターゲットを指定する

ユーザーがビューの上にドロップ シャドウを解放するときは、データを受け取って正しく応答するように、ビューを適切に構成する必要があります。

DropHelper.configureView() は、ドロップ ターゲットを指定できる静的なオーバーロード メソッドです。パラメータには次のものがあります。

たとえば、画像を受け入れるドロップ ターゲットを作成するには、次のいずれかのメソッド呼び出しを使用します。

Kotlin

configureView(
    myActivity,
    targetView,
    arrayOf("image/*"),
    options,
    onReceiveContentListener)

// or

configureView(
    myActivity,
    targetView,
    arrayOf("image/*"),
    onReceiveContentListener)

Java

DropHelper.configureView(
    myActivity,
    targetView,
    new String[] {"image/*"},
    options,
    onReceiveContentlistener);

// or

DropHelper.configureView(
    myActivity,
    targetView,
    new String[] {"image/*"},
    onReceiveContentlistener);

2 番目の呼び出しでは、ドロップ ターゲットの設定オプションが省略されています。この場合、ドロップ ターゲットのハイライト カラーはテーマのセカンダリ(アクセント カラー)に設定され、ハイライトの角の丸みは 16 dp に設定され、EditText コンポーネントのリストは空になります。詳しくは、次のセクションをご覧ください。

ドロップ ターゲットを構成する

DropHelper.Options 内部クラスを使用すると、ドロップ ターゲットを構成できます。クラスのインスタンスを DropHelper.configureView(Activity, View, String[], Options, OnReceiveContentListener) メソッドに渡します。詳しくは、前のセクションをご覧ください。

ドロップ ターゲットのハイライト表示をカスタマイズする

DropHelper は、ユーザーがターゲット上にコンテンツをドラッグしたときにハイライト表示されるように、ドロップ ターゲットを設定します。DropHelper はデフォルトのスタイルを提供し、DropHelper.Options はハイライトの色を設定し、ハイライトの長方形の角の半径を指定します。

次の例に示すように、DropHelper.Options.Builder クラスを使用して DropHelper.Options インスタンスを作成し、構成オプションを設定します。

Kotlin

val options: DropHelper.Options = DropHelper.Options.Builder()
                                      .setHighlightColor(getColor(R.color.purple_300))
                                      .setHighlightCornerRadiusPx(resources.getDimensionPixelSize(R.dimen.drop_target_corner_radius))
                                      .build()

Java

DropHelper.Options options = new DropHelper.Options.Builder()
                                     .setHighlightColor(getColor(R.color.purple_300))
                                     .setHighlightCornerRadiusPx(getResources().getDimensionPixelSize(R.dimen.drop_target_corner_radius))
                                     .build();

ドロップ ターゲットの EditText コンポーネントを処理する

DropHelper は、ターゲットに編集可能なテキスト フィールドが含まれている場合、ドロップ ターゲット内のフォーカスも制御します。

ドロップ ターゲットは単一のビューまたはビュー階層のいずれかです。ドロップ ターゲットのビュー階層に 1 つ以上の EditText コンポーネントが含まれている場合は、コンポーネントのリストを DropHelper.Options.Builder.addInnerEditTexts(EditText...) に渡して、ドロップ ターゲットのハイライト表示とテキストデータ処理が正しく機能するようにします。

DropHelper は、ドロップ ターゲットのビュー階層内の EditText コンポーネントが、ドラッグ操作中に、含まれているビューからフォーカスを盗むのを防ぎます。

また、ドラッグ&ドロップ ClipData にテキストと URI データが含まれている場合、DropHelper は、ドロップ ターゲットの EditText コンポーネントのいずれかを選択してテキストデータを処理します。選択は、次の優先順位に基づいて行われます。

  1. ClipData がドロップされる EditText
  2. テキスト カーソル(キャレット)を含む EditText
  3. DropHelper.Options.Builder.addInnerEditTexts(EditText...) の呼び出しに指定された最初の EditText

EditText をデフォルトのテキストデータ ハンドラとして設定するには、DropHelper.Options.Builder.addInnerEditTexts(EditText...) の呼び出しの最初の引数として EditText を渡します。たとえば、ドロップ ターゲットで画像を処理できるものの、編集可能なテキスト フィールド T1T2T3 が含まれている場合は、次のようにして T2 をデフォルトにします。

Kotlin

val options: DropHelper.Options = DropHelper.Options.Builder()
                                      .addInnerEditTexts(T2, T1, T3)
                                      .build()

Java

DropHelper.Options options = new DropHelper.Options.Builder()
                                     .addInnerEditTexts(T2, T1, T3)
                                     .build();

ドロップ ターゲットのデータを処理する

DropHelper.configureView() メソッドは、ドラッグ&ドロップの ClipData を処理するために作成した OnReceiveContentListener を受け入れます。ドラッグ&ドロップ データは、ContentInfoCompat オブジェクトでリスナーに提供されます。オブジェクト内にテキストデータが存在します。画像などのメディアは URI で表されます。

OnReceiveContentListener は、DropHelper.configureView() を使用して次のタイプのビューを構成した場合、ドラッグ&ドロップ以外のユーザー操作(コピー&ペーストなど)によってドロップ ターゲットに渡されるデータも処理します。

  • すべてのビュー(ユーザーが Android 12 以降を実行している場合)。
  • AppCompatEditText(Android 7.0 より前のバージョンの Android を使用している場合)。

MIME タイプ、権限、コンテンツの検証

DropHelper による MIME タイプの確認は、ドラッグ&ドロップ データを提供するアプリが作成するドラッグ&ドロップ ClipDescription に基づいています。ClipDescription を検証して、MIME タイプが正しく設定されていることを確認します。

DropHelper は、ドラッグ&ドロップ ClipData に含まれるコンテンツ URI に対するすべてのアクセス権をリクエストします。詳細については、DragAndDropPermissions をご覧ください。この権限により、ドラッグ&ドロップ データを処理するときにコンテンツ URI を解決できます。

DropHelper は、ドロップされたデータの URI を解決する際に、コンテンツ プロバイダから返されたデータを検証しません。null をチェックし、解決されたデータの正確性を検証します。