ドラッグ&ドロップ

Jetpack Compose では、ドラッグ&ドロップで次の 2 つの修飾子がサポートされています。

  • dragAndDropSource: ドラッグ ジェスチャーの開始点としてコンポーザブルを指定します
  • dragAndDropTarget: ドロップされたデータを受け入れるコンポーザブルを指定します。

たとえば、ユーザーがアプリ内で画像をドラッグできるようにするには、画像コンポーザブルを作成し、dragAndDropSource 修飾子を追加します。ドロップ ターゲットをセットアップするには、別の画像コンポーザブルを作成し、dragAndDropTarget 修飾子を追加します。

修飾子は、複数のドラッグ ソースと複数のドロップ ターゲットに適用できます。

修飾子により、アプリは View 実装と相互運用可能な ClipData を使用して、2 つ以上のコンポーザブル間でデータを共有できます。

ドラッグ イベントを開始する

コンポーネント内でドラッグ イベントを有効にするには、dragAndDropSource 修飾子を追加します。この修飾子は suspend 関数をパラメータとして受け取ります。この関数は、ドラッグ オペレーションを開始するユーザー操作を定義します。dragAndDropSource 修飾子は、ポインタ入力イベントを受け取るまで待機し、イベント ハンドラに渡されたラムダを実行します。このラムダを使用して、タップや長押しなどのさまざまな入力イベントを検出します。詳細については、Compose でのポインタ入力をご覧ください。

ポインタ入力イベントは通常、次のように実装された長押しです。

Modifier.dragAndDropSource {
    detectTapGestures(onLongPress = {
        // Transfer data here.
    })
}

ドラッグ&ドロップ セッションを開始するには、startTransfer() 関数を呼び出します。このスコープ内で、DragAndDropTransferData を使用して転送可能なデータを表します。リモート URI、クリップボード上のリッチテキスト データ、ローカル ファイルなどのデータを使用できますが、それらはすべて ClipData オブジェクトでラップする必要があります。たとえば、次のように書式なしテキストを指定します。

Modifier.dragAndDropSource {
    detectTapGestures(onLongPress = {
        startTransfer(
            DragAndDropTransferData(
                ClipData.newPlainText(
                    "image Url", url
                )
            )
        )
    })
}

ドラッグ アクションがアプリの境界をまたがるようにするため、DragAndDropTransferData コンストラクタは flags 引数を受け入れます。次の例では、DRAG_FLAG_GLOBAL 定数で、アプリ間でデータをドラッグできることを指定しています。

Modifier.dragAndDropSource {
    detectTapGestures(onLongPress = {
        startTransfer(
            DragAndDropTransferData(
                ClipData.newPlainText(
                    "image Url", url
                ),
                flags = View.DRAG_FLAG_GLOBAL
            )
        )
    })
}

DragAndDropTransferData は、Android ビューシステムでサポートされているフラグを受け入れます。使用可能なフラグの一覧については、View 定数のリストをご覧ください。

ドロップデータを受信する

コンポーザブルに dragAndDropTarget 修飾子を割り当て、コンポーザブルがドラッグ&ドロップ イベントを受信できるようにします。この修飾子には 2 つのパラメータがあります。1 つ目のパラメータはフィルタとして機能し、修飾子が受け入れ可能なデータの種類を指定します。2 つ目のパラメータはコールバックでデータを提供します。

コールバック インスタンスは覚えておく必要があります。次のスニペットは、コールバックを覚える方法を示しています。

val callback = remember {
    object : DragAndDropTarget {
        override fun onDrop(event: DragAndDropEvent): Boolean {
            // Parse received data
            return true
        }
    }
}

次のスニペットは、ドロップされた書式なしテキストを処理する方法を示しています。

Modifier.dragAndDropTarget(
    shouldStartDragAndDrop = { event ->
        event.mimeTypes().contains(ClipDescription.MIMETYPE_TEXT_PLAIN)
    }, target = callback
)

コールバック関数は、イベントが消費された場合は true を返し、イベントが拒否されて親コンポーネントに伝播されない場合は false を返します。

ドラッグ&ドロップ イベントを処理する

UI とアプリの動作を正確に制御するために、DragAndDropTarget インターフェースのコールバックをオーバーライドして、ドラッグ&ドロップ イベントの開始、終了、コンポーネントへの出入りのタイミングを監視します。

object : DragAndDropTarget {
    override fun onStarted(event: DragAndDropEvent) {
        // When the drag event starts
    }

    override fun onEntered(event: DragAndDropEvent) {
        // When the dragged object enters the target surface
    }

    override fun onEnded(event: DragAndDropEvent) {
        // When the drag event stops
    }

    override fun onExited(event: DragAndDropEvent) {
        // When the dragged object exits the target surface
    }

    override fun onDrop(event: DragAndDropEvent): Boolean = true
}

参考情報

Codelab: Compose でのドラッグ&ドロップ