LoginSignup
3
2

More than 1 year has passed since last update.

Jetpack Compose1.2.0で追加されたmovableContentOfを使ってみた

Last updated at Posted at 2022-11-21

今回は、Jetpack Compose1.2.0で追加されたmovableContentOfについて解説していきたいと思います。

1.2.0で追加されたのですが、自分は完全に見落としていました。
1.3.0で追加されたLookaheadLayoutと合わせて使うと、
これまでComposeで実現しづらかった、Shared Element Transitionを実現できるみたいです。

本記事では、まずmovableContentOfについて見ていきたいと思います。

movableContentOfとは何か

movableContentOfとは何かというと、公式サイトによれば

コンポーザブル ラムダを別のラムダに変換します。変換されたラムダは、状態および対応するノードを呼び出された新しい場所に移動します。前の呼び出しがコンポジションから出ると状態は一時的に保持され、ラムダへの新しい呼び出しがコンポジションに入った場合、状態および関連付けられたノードが新しい呼び出しの場所に移動されます。

とのことです。

Compose関数の状態を保持して、
状況に応じて、関数の呼び出し場所を変えたりできるみたいですね。

これだけだと、どう使うのかイメージつきづらいので、
実際にmovableContentOfを使った実装をもとに、解説します。

movableContentOfの使い方

写真のように、Checkboxが縦に並んでおり、
Buttonによって、縦並びと横並びを切り替えられる画面を作りたいと思います。

まずは、movableContentOfを使わない方法だと、こうなります。

↓ movableContentOfを使わないパターン

@Composable
fun MovableContentOfSample() {

    MaterialTheme {
        Surface(
            modifier = Modifier.fillMaxSize()
        ) {
            var isVertical: Boolean by remember {
                mutableStateOf(true)
            }

            Column(
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Button(onClick = {
                    isVertical = isVertical.not()
                }) {
                    Text(
                        text = if (isVertical) {
                            "Horizontal"
                        } else {
                            "Vertical"
                        }
                    )
                }
                Spacer(modifier = Modifier.height(8.dp))
                CheckBoxColumnAndRow(isVertical = isVertical) {
                    (1..3).forEach { _ ->
                        var checked: Boolean by remember {
                            mutableStateOf(false)
                        }
                        Checkbox(checked = checked, onCheckedChange = {
                            checked = it
                        })
                    }
                }
            }
        }
    }
}

@Composable
private fun CheckBoxColumnAndRow(
    isVertical: Boolean,
    content: @Composable () -> Unit
) {
    if (isVertical) {
        Column {
            content()
        }
    } else {
        Row {
            content()
        }
    }
}

一見できているように見えますが、
Buttonを押すたびに、Checkboxのチェック状態がクリアされてしまいます。
az_recorder_20221121_153909_edited.gif

これは、isVerticalの値が変わるたびに、
CheckBoxColumnAndRowのcontentで受け取っている、Composableラムダの状態が保持されないためです。

movableContentOfを使うと解決できます。

↓ movableContentOfを使うパターン

@Composable
fun MovableContentOfSample() {

    MaterialTheme {
        Surface(
            modifier = Modifier.fillMaxSize()
        ) {
            var isVertical: Boolean by remember {
                mutableStateOf(true)
            }

            val content = remember {
                movableContentOf {
                    (1..3).forEach { _ ->
                        var checked: Boolean by remember {
                            mutableStateOf(false)
                        }
                        Checkbox(checked = checked, onCheckedChange = {
                            checked = it
                        })
                    }
                }
            }

            Column(
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Button(onClick = {
                    isVertical = isVertical.not()
                }) {
                    Text(
                        text = if (isVertical) {
                            "Horizontal"
                        } else {
                            "Vertical"
                        }
                    )
                }
                Spacer(modifier = Modifier.height(8.dp))
                CheckBoxColumnAndRow(
                    isVertical = isVertical,
                    content = content
                )
            }
        }
    }
}

@Composable
private fun CheckBoxColumnAndRow(
    isVertical: Boolean,
    content: @Composable () -> Unit
) {
    if (isVertical) {
        Column {
            content()
        }
    } else {
        Row {
            content()
        }
    }
}

Buttonの押下前後で、Checkboxのチェック状態が保持されているのが分かります。

az_recorder_20221121_155722_edited.gif

正直、これだけならmovableContentOfを使わずとも、
ViewModelで状態を保持すれば済む話ですが、

movableContentOfは、
例えば一覧画面と詳細画面で、Composableラムダを共有するといった、
画面をまたいだ共有も実現できます。
なので、Shared Elementのような実装を実現できるということですね。

おわりに

今回は、movableContentOfについて解説しました。
時間があったら、LookaheadLayoutとmovableContentOfを組み合わせた、
Shared Element Transitionについても解説したいと思います。

3
2
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
3
2