はじめに

UI をドラッグ操作で動かしたい。今回は要素の並び方を元にしてUIを構築する Jetpack Compose でどのように実現できるのか調べました。

modifier の pointerInput

最初に Composable の modifier でドラッグ移動を実現する方法について見てみます。

@Composable
fun DragMe() {
    var offserX by remember { mutableStateOf(0) }
    var offserY by remember { mutableStateOf(0) }

    Text(
        text = "Drag Me",
        modifier = Modifier
            .offset{ IntOffset(offserX, offserY) }
            .pointerInput(Unit) {
                detectTransformGestures { change, dragAmount, _ , _ ->
                    offserX += dragAmount.x.toInt()
                    offserY += dragAmount.y.toInt()
                }
            }
            .background(Color.LightGray)
    )
}

最初に、xとyの offset を保持、監視するために、remember mutableStateOf を使用して宣言しておきます。

次に、Text の modifier で offset に それらを渡します。

あとは offset の値をドラッグ操作で更新するだけです。

modifier の pointerInput は key と suspend fun の block を受け取ります。

key はポインター入力の種類を識別するための引数です(タッチ、マウス、ペン など…)。基本的には Unit が渡されます。

ドラッグ操作を行っている間だけ関数を動作させるため、引数に渡すのは suspend fun になります。suspend fun は中断可能な関数です。

detectTransformGestures はポインターの変換ジェスチャ(ドラッグやスケーリングなど…)を検出するメソッドで、これを用いてoffserを更新します。

4つのラムダ式が含まれますが、今回はドラッグの量を示す Offset のオブジェクトである dragAmount を使います。x,y の offset の値を dragAmount でそれぞれ更新します。


好きな Composable を簡単に動かしたい

先ほどの方法では、Composable 毎に modifier を書くことになるのでコードの再利用がしにくいです。

そこで、動かしたい Composable 関数をラップする Composable を用意してみます。

@Composable
fun DragMe2(content: @Composable (Modifier) -> Unit) {
    var offserX by remember { mutableStateOf(0) }
    var offserY by remember { mutableStateOf(0) }

    Column(
        modifier = Modifier
            .offset { IntOffset(offserX, offserY) }
            .pointerInput(Unit) {
                detectTransformGestures { _, dragAmount, _, _ ->
                    offserX += dragAmount.x.toInt()
                    offserY += dragAmount.y.toInt()
                }
            }
    ) {
        content(Modifier)
    }
}