【正社員】還元率83%【フリーランス】マージン一律5万円で案件をご紹介させていただきます。 詳細はこちら

【Android】<Kotlin>Jetpack Composeの再コンポーズについて仕組みと注意点を徹底解説

【Android】<Kotlin>Jetpack Composeの再コンポーズについて仕組みと注意点を徹底解説
すだ

みなさまこんにちは〜!
メモリアインクのすだです。

今回は、UI部分の実装に使用するJetpack Composeにおいて
再コンポーズとは何か、どういう時に起こるのか、そして起こった時にUIに起こる変化について解説していきます。

この記事を読んでわかること…
・再コンポーズとは
・再コンポーズが起こるタイミングについて
・再コンポーズ時の最適化について

目次

環境

  • Kotlin (ver 1.9.0)
  • Android Studio (Giraffe | 2022.3.1 Patch 3)

再コンポーズとは(基本の概念)

再コンポーズ(Recomposition)とは、Jetpack Compose で UI が更新される仕組みのことです。

Composable 関数は、状態(State)が変化したときに再び呼び出され、画面を新しい状態で描画しなおします。これが「再コンポーズ」です。

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }

    Column {
        Text("回数: $count") // ← ここが再コンポーズされる

        Button(onClick = { count++ }) {
            Text("増やす")
        }
    }
}

この場合、count++ が呼ばれるたびに count の値が変わるので
それをJetpackComposeが自動で判別してText("回数: $count") という部分を再コンポーズ =「もう一度実行して、新しい count に基づいた UI を再描画」します。

このとき、Button などの他の部品や Column全体のUIは再コンポーズされず
あくまでText()だけ再コンポーズ=再描画されるのです。

再コンポーズが起きる主なタイミング

まとめると、以下のような場面でComposableは再コンポーズされます。

State が変わったとき

count の値が変わると、それを使っている Text() が再コンポーズされる

var count by remember { mutableStateOf(0) }

Button(onClick = { count++ }) {
    Text("回数: $count")
}

② 親Composableが再コンポーズされたとき

親が再コンポーズされれば、子も再コンポーズされる(必要に応じて)

@Composable
fun Parent() {
    val name = "Hanako"
    Child(name)
}

@Composable
fun Child(name: String) {
    Text("こんにちは、$name")
}

③ ダークモード切り替え、フォントサイズ変更など UIテーマが変わったとき

システムの変更があれば、全体が再コンポーズされます(設定画面から戻ってきたときなど)

④ 画面遷移して戻ってきたとき

NavHost による画面遷移で、「ある画面」に戻ってきたらそのComposableが再コンポーズされます。

⑤ キーボードの表示 / 非表示など、UIのサイズが変わるとき

レイアウトが大きく変わると、該当部分が再構築されることがあります。

再コンポーズを最適化するためのテクニック

remember

Composeでは再コンポーズが頻繁に起きますが、毎回重たい処理が走るとパフォーマンスが悪化します。
remember を使うことで 「無駄な再計算」を防ぎ、パフォーマンスを維持できます。

例:カラーパレットを表示する画面 ▼

@Composable
fun ColorPaletteScreen() {
    // 毎回再コンポーズが起きても、colors の再生成は防がれる
    val colors = remember {
        listOf(Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Cyan)
    }

    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        items(colors) { color ->
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(60.dp)
                    .padding(vertical = 8.dp)
                    .background(color)
            )
        }
    }
}

もし val colors = listOf(...)remember を使わずに定義していた場合、
再コンポーズのたびに listOf(...) が再実行されて、毎回新しいリストが生成されてしまいます。

remember を使うことで、Composableが再描画されても、最初の1回だけリストを生成し、以後はキャッシュされたものを使い続けてくれます。

derivedStateOf

Compose では、複数の State を使っていると、状態の依存関係が複雑になります。
そこで derivedStateOf を使うと、「この状態はこの値から自動で決まるよ」と明示できるようになります。

例:カウントが偶数か奇数かで背景色を切り替える ▼

@Composable
fun EvenOddCounter() {
    var count by remember { mutableStateOf(0) }

    // count の変化に応じて isEven を更新
    val isEven by remember(count) {
        derivedStateOf { count % 2 == 0 }
    }

    val backgroundColor = if (isEven) Color.LightGray else Color(0xFFFFE0B2) // オレンジっぽい色

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(backgroundColor)
            .padding(32.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("現在のカウント: $count", fontSize = 20.sp)
        Spacer(modifier = Modifier.height(16.dp))
        Button(onClick = { count++ }) {
            Text("カウントを増やす")
        }
    }
}

isEvenの意味としては、「count が偶数のときだけ isEven は true に、奇数のときは false になる。」
それにderivedStateOfをつけると、依存元 (count) に変化があったときのみ派生状態 (isEven) を再評価してくれるのです。

val isEven = count % 2 == 0仮にこう書いてしまうと、countが変わっていなくても別の要因で再コンポーズが起きたときにこの処理が走って再計算されてしまうため)

まとめ

おつかれさまでした。いかがでしたでしょうか!

再コンポーズは
・状態が変わったとき
・ 親Composableが変わったとき
・ テーマや画面の条件が変わったとき
に「賢く必要なところだけ再描画するしくみ」であるということを覚えてください!

すだ

技術者としてのキャリアパスを次のレベルへと進めたい皆様、
未経験からIT・Webエンジニアを目指すなら【ユニゾンキャリア】
を通じて、
自分の市場価値をさらに向上させてみませんか?

それではまた次回の記事でお会いしましょう!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

弊社テックブログをご愛読いただきありがとうございます。
当テックブログを運用している株式会社メモリアインクは、
【正社員】還元率83%
【フリーランス】マージン一律5万円で案件のご紹介
と、エンジニアの皆様に分かりやすい形で稼げる仕組みを構築し提供させていただいております。

コメント

コメントする

目次