LoginSignup
1
0

ComposeのDragAndDrop系のModifier

Posted at

compose foundation 1.6 で Experimentalだったドラッグ&ドロップ周りのModifierが来てたのでいくつか記事にします(させます)

dragAndDropSource

この機能を利用すると、任意のComposableにドラッグ動作を提供できます。以下に一般的なAPIの形式を示します。

@ExperimentalFoundationApi
fun Modifier.dragAndDropSource(
    drawDragDecoration: DrawScope.() -> Unit,
    block: suspend DragAndDropSourceScope.() -> Unit
): Modifier

例: 文字をドラッグ可能にする

画面収録 2024-01-07 8.44.00.gif

下記のコンポーザブル関数例では、ドラッグして文字列の転送を開始するボックスが定義されています。

@Composable
fun DragAndDropSourceSample() {
    val color = Color.Red
    Box(
        modifier = Modifier
            .size(56.dp)
            .background(color = color)
            .dragAndDropSource(
                drawDragDecoration = { drawRect(color) },
            ) { // DragAndDropSourceScope
                detectTapGestures(
                    onLongPress = {
                        startTransfer( // 送るデータ
                            transferData = DragAndDropTransferData(
                                ClipData.newPlainText("text", "text"),
                            ),
                        )
                    }
                )
            }
    )
}

drawDragDecorationはドラッグ中の視覚効果をDrawScopeで描画します。また、DragAndDropSourceScopePointerInputScopeが提供され、これを用いてどのジェスチャでstartTransfer(データ転送)をするかを定義します。

dragAndDropTargetを用いたドロップ操作の追加

ドラッグ対象に対して、以下のような形式でdragAndDropTarget機能を追加することができます。

@ExperimentalFoundationApi
fun Modifier.dragAndDropTarget(
    shouldStartDragAndDrop: (startEvent: DragAndDropEvent) -> Boolean,
    target: DragAndDropTarget
): Modifier

dragTarget.gif

アンカーを設定するDraggableAnchors関数

@ExperimentalFoundationApi
fun <T : Any> DraggableAnchors(
    builder: DraggableAnchorsConfig<T>.() -> Unit
): DraggableAnchors<T> = MapDraggableAnchors(DraggableAnchorsConfig<T>().apply(builder).anchors)

特定の位置にアンカーを設定し、そのアンカー間でのドラッグを可能にするDraggableAnchors関数についても触れておくと良いでしょう。それにより、ユーザーは特定の範囲内でコントロールの移動を体験することができます。

DraggableAnchors {
    T at 0f
    T at 300f
}

上記のようにアンカーを設定し、anchoredDraggableを使って制御することができます。

例えば、以下のコードはスタートとエンドの位置にアンカーを設定し、アイコンをそれらのアンカー間でドラッグできるようにしています。

Anchored (1).gif

enum class AnchorPos {
    Start, End;
}
...
val density = LocalDensity.current
val draggableState = remember {
    AnchoredDraggableState(
        initialValue = AnchorPos.Start,
        anchors = DraggableAnchors {
            AnchorPos.Start at 0f
            AnchorPos.End at 300f
        },
        positionalThreshold = { totalDistance -> totalDistance * 0.5f},
        velocityThreshold = { with(density) { 100.dp.toPx()} },
        animationSpec = tween(),
        confirmValueChange = { true }
    )
}
Box(modifier = Modifier.fillMaxSize()) {
    Image(
        imageVector = Icons.Default.FavoriteBorder,
        contentDescription = "",
        modifier = Modifier
            .offset(
                x = with(density) {
                    draggableState
                        .requireOffset()
                        .toDp()
                }
            )
            .anchoredDraggable(
                state = draggableState,
                orientation = Orientation.Horizontal,
                enabled = true,
                reverseDirection = false,
                interactionSource = null
            )
            .padding(start = 24.dp)
    )
}

これらの機能を駆使することで、Jetpack Compose におけるユーザーインタラクションは一層豊かになります。機能の実装にあたっては、常にユーザビリティを考慮し、最適なユーザーエクスペリエンスを提供することが重要です。

終わり

この記事は、雑メモをGPT4に通して記事っぽくしてもらってます。
draggable2D, draggable もメモにあったのですが長すぎて削られた様子。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0