チュートリアルのJetpack Compose の状態がわかりやすいかも
チュートリアルの内容をざっくり簡略化して確認
Flow / LiveDataは別途確認
状態の仕組みについて
動作しない例
TemplateのGreeting()を流用して以下のようなボタンを押すとラベルが変わる実装をします
ただしこの実装ではラベルは何も変わりません
var nameがComposeの状態変更の検出対象になってないからです
@Composable fun AppContent() {
var name = "Android"
MaterialTheme {
Surface {
Column {
Button(onClick = { name = "test" }) {
Text("click")
}
Greeting(text)
}
}
}
}
State
State<T>にすることでComposeの状態変更の検出対象にすることができます
val name: MutableState<String> = mutableStateOf("Android")
@Composable fun AppContent() {
MaterialTheme {
Surface {
Column {
Button(onClick = { name.value = "test" }) {
Text("click")
}
Greeting(name.value)
}
}
}
}
by(delegate property)を使用すればvalueではなく直接代入/参照できます
getValue/setValueのimportが必要です
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
var name by mutableStateOf("Android")
@Composable fun AppContent() {
MaterialTheme {
Surface {
Column {
Button(onClick = { name = "test" }) {
Text("click")
}
Greeting(name)
}
}
}
}
remember
rememberを使用すると@Composable内部でのみ使用可能となりパフォーマンスが改善されます
詳しくは以下を参照
https://developer.android.com/jetpack/compose/performance?hl=ja
@Composable fun AppContent() {
var name by remember { mutableStateOf("Android") }
...
}
rememberSaveable
rememberの場合Android実機側で自動回転をONにして回転させるとActivityが再作成されるため値が元に戻ります
rememberSaveableを使用することで対応できます
※ rememberを使用してない場合もrememberSaveable同様に値が保持されてます
※ rememberSaveableの場合でもアプリ終了時後は値は保持されてません
@Composable fun AppContent() {
var name by rememberSaveable { mutableStateOf("Android") }
...
}
複数のメソッドをまたぐ場合(状態ホイスティング)
複数のメソッドをまたぐ場合以下のような実装はできません
@Composable fun AppContent() {
var name by remember { mutableStateOf("Android") }
MaterialTheme {
Screen(name)
}
}
@Composable fun Screen(name: String) {
Surface {
Column {
//Error: nameはvalのため代入できない
Button(onClick = { name = "test" }) {
Text("click")
}
Greeting(name)
}
}
}
状態ホイスティングを使用して実装します
@Composable fun AppContent() {
var name by remember { mutableStateOf("Android") }
MaterialTheme {
Screen(name) {
name = it
}
}
}
@Composable fun Screen(name: String, onClick: (String) -> Unit) {
Surface {
Column {
Button(onClick = { onClick("test") }) {
Text("click")
}
Greeting(name)
}
}
}
クラスを渡す場合(状態ホルダー)
クラスを渡す場合以下のような実装ではComposeの状態変更の検出対象にすることができません
※ datasにDatasクラスを渡せば検出されますが使い勝手が悪すぎる
class Datas (var name: String, var num: Int = 0)
@Composable fun AppContent() {
val datas by remember { mutableStateOf(Datas("Android")) }
...
}
以下のように状態ホルダーを作成して対応します
詳しくはチュートリアルのJetpack Compose の高度な状態と副作用を参照
class Datas {
var name by mutableStateOf("")
var num = 0
constructor(name: String, num: Int) {
this.name = name
this.num = num
}
}
@Composable fun rememberDatas(name: String, num: Int = 0): Datas = remember { Datas(name, num) }
@Composable fun AppContent() {
val datas: Datas = rememberDatas("Android")
...
}
rememberSaveableの場合は置換えただけでは不十分でカスタムのセーバーの作成が必要です
class Datas {
var name by mutableStateOf("")
var num = 0
constructor(name: String, num: Int = 0) {
this.name = name
this.num = num
}
companion object {
val Saver: Saver<Datas, *> = listSaver(
save = { listOf(it.name, it.num) },
restore = {
Datas(name = it[0] as String, num = it[1] as Int)
}
)
}
}
@Composable fun rememberDatas(name: String): Datas = rememberSaveable(saver = Datas.Saver) { Datas(name) }
@Composable fun AppContent() {
val datas: Datas = rememberDatas("Android")
...
}
Android Studio Dolphin 2021.3.1 Patch 1 built on September 30, 2022