🐡

Jetpack Composeで状態を扱う

2023/08/20に公開

Overview

https://developer.android.com/jetpack/compose/state?hl=ja
公式より引用
アプリにおいて状態とは、時間とともに変化する可能性がある値すべてを指します。これは非常に広範な定義であり、Room データベースにも、クラス内の変数一つにも当てはまります。

すべての Android アプリはユーザーに状態を表示します。Android アプリの状態の例を次にいくつか示します。

ネットワーク接続を確立できないときに表示されるスナックバー。
ブログ投稿と関連コメント。
ユーザーがボタンをクリックしたときに再生されるボタンの波紋アニメーション。
ユーザーが画像の上に描画できるステッカー。
Jetpack Compose では、Android アプリが状態をどこで、どのように保存し、使用するかの設定を明示的に行うことができます。このガイドでは、状態とコンポーザブルの関係に加え、Jetpack Compose が提供する、状態を簡単に処理するための API を中心に説明します。

今回は、MutableStateを使って状態を扱い、カウンターが増えるロジックを作ってみます。

summary

公式によると

コンポーザブル内の状態
コンポーズ可能な関数は、remember API を使用してオブジェクトをメモリに格納できます。初回コンポーズの際に、remember によって計算された値がコンポジションに保存され、保存された値は再コンポーズの際に返されます。remember を使用すると、可変オブジェクトと不変オブジェクトの両方を保存できます。

mutableStateOfを使用するには、rememberで囲む必要があります。
値にアクセスするときは、.valueを使用します。

全体のコード

package com.example.basiccodelab

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.example.basiccodelab.ui.theme.BasicCodelabTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicCodelabTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting(name = "Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    // rememberで、mutableStateOfを使うと、値の変更をComposableが再描画する
    var counter = remember {
        mutableStateOf(0)
    }
    Button(onClick = {
        // .valueで値を取得、.value = で値を変更
        counter.value++
    }) {
        // Textの引数には、Int型のcounter.valueを渡している
        Text(text = "I've been clicked ${counter.value} times")
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    BasicCodelabTheme {
        Greeting(name = "Android")
    }
}

valueを使わない方法

変数の =by に変更して、赤い文字のところをoption + Enterを押して、setValuegetValueをimportすると、valueを使わなくても状態を操作できるようになります。

// rememberで、mutableStateOfを使うと、値の変更をComposableが再描画する
    var counter by remember {
        mutableStateOf(0)
    }
    Button(onClick = {
        // .valueで値を取得、.value = で値を変更
        counter++
    }) {
        // Textの引数には、Int型のcounter.valueを渡している
        Text(text = "I've been clicked ${counter} times")
    }

valueを使用しない全体のコード

package com.example.basiccodelab

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.example.basiccodelab.ui.theme.BasicCodelabTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicCodelabTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting(name = "Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    // rememberで、mutableStateOfを使うと、値の変更をComposableが再描画する
    var counter by remember {
        mutableStateOf(0)
    }
    Button(onClick = {
        // .valueで値を取得、.value = で値を変更
        counter++
    }) {
        // Textの引数には、Int型のcounter.valueを渡している
        Text(text = "I've been clicked ${counter} times")
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    BasicCodelabTheme {
        Greeting(name = "Android")
    }
}

数字が増減するボタンを作った

ボタンをクリックすると、数字が増えたり、減ったりするボタンを作ってみました。

package com.example.basiccodelab

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.basiccodelab.ui.theme.BasicCodelabTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicCodelabTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting(name = "Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Column {
        // rememberで、mutableStateOfを使うと、値の変更をComposableが再描画する
        var counter by remember {
            mutableStateOf(0)
        }
        Button(onClick = {
            // .valueで値を取得、.value = で値を変更
            counter++
        }) {
            // Textの引数には、Int型のcounter.valueを渡している
            Text(text = "数字が増える: ${counter}")
        }
        Spacer(modifier = androidx.compose.ui.Modifier.height(16.dp))
        Button(onClick = {
            // .valueで値を取得、.value = で値を変更
            counter--
        }) {
            // Textの引数には、Int型のcounter.valueを渡している
            Text(text = "数字が減る: ${counter}")
        }
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    BasicCodelabTheme {
        Greeting(name = "Android")
    }
}

プラス

マイナス

thoughts

今回、MutableStateを使ってみた感想ですが、FlutterのsetState、ReactのuseStateと同じように、状態を保持して、操作することができる機能があるのだなと、理解できました。
最近の技術は概念が似ているので、理解しやすいですね。やっていることは同じ。

学習の参考になった動画
https://www.youtube.com/watch?v=YZibyXeXv9E

Jboy王国メディア

Discussion