ViewModelのことがわかってきたので、忘れないよう記録しておく
クリックするたびボタンのテキストがA→B→Aのように変わるボタンを実装
ViewModelを使わない場合
MainActivityクラスの下にOtameshiApp関数を追加。
(既存のGreetingなんちゃらの中に記述してもいいけど!)
Column設定して(面倒ならmodifierはいらない)、ボタンを1つ設定。
ボタンのテキスト用の変数buttonText
を宣言。
ボタンのonClick
に直接ボタンのテキストを変更する処理を記述
@Composable
fun OtameshiApp(modifier: Modifier = Modifier) {
Column(
modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var buttonText by remember { mutableStateOf("A") }
Button(
onClick = { buttonText = if (buttonText == "A") "B" else "A" },
modifier= Modifier.size(width= 200.dp, height = 50.dp)
) {
Text(text = buttonText,fontSize=30.sp)
}
}
}
これを実行して、ボタンをポチポチすると、A→B→A→Bとボタンのテキストが変わる。
ボタンをBにしたまま、画面を回転させると・・・
Aに戻る不思議
この問題は変数宣言時に'remember'ではなく
var buttonText by rememberSaveable { mutableStateOf("A") }
とすると解決するが、根本解決にはなってない。
なぜならこの画面でボタンをBにしてから、別の画面に移動し、再度この画面に戻ってきたとき、Aボタンに戻ってしまうから。
(別の画面肉と値が保持されないので)
他にも、同じボタンをこの画面と別画面で共有したくても、画面ごとでしか状態が保持できないので一貫性を保つのが難しい。
あと、テストでもremember {mutableStateOf("A"}
を直接使うことができないので、状態のテストがやりづらい。
これらの問題がViewModelを使うと全部解決できてしまう!
ViewModelを使う場合のコード例
1.gradleファイルに依存関係を追加
なんか、implementationの書き方が変わった。
簡単になった!°˖☆◝(⁰▿⁰)◜☆˖°
dependencies {
//割愛
implementation(libs.lifecycle.viewmodel.compose)
//割愛
}
追加したら、SyncNowする。
ほんと、よく忘れるので、やる。絶対。
2.画面と分離させるため、新しいKotolinファイルを作る
model用のパッケージ作って、その中にファイル作るのが正しいと思うけど、テスト的に作ってるアプリなので、いったん別ファイルにしておけばOKということで!
3.状態を管理するクラスを作る
1個しか管理する項目がないのに作るんかーい!って気持ちになったけど、増えるかもしれないから。
data class OtameshiUiState(
val buttonText:String = "A"
)
4.ViewModelのクラスを作る
とりあえず、おまじないのように以下を記述
class OtameshiViewModel:ViewModel() {
private val _uiState = MutableStateFlow(OtameshiUiState())
val uiState: StateFlow<OtameshiUiState> = _uiState.asStateFlow()
//AボタンをBボタンに変更する処理
fun changeButtonText() {
_uiState.update {
it.copy(buttonText = if (it.buttonText == "A") "B" else "A")
}
}
}
1行目のuiState
は、MutableStateFlow
なので内容を変更できるし、変更したら画面側に通知する仕組みになっている。
ただし、外部から不用意に値を変えられるといけないので、このクラス内でしか値が変えられないようにしている。
外部用には2行目の読み取り専用のuiState
を使う。
その下のchangeButtonText()
メソッドは、ボタンをクリックしたときにボタンのテキストがAだったらBへ、BだったらAに変更する処理
5.画面側にViewModelを設定
一番最初に作ったOtameshiAppをviewModelとアクセスできるよう変更していく。
@Composable
fun OtameshiApp(
modifier: Modifier = Modifier,
viewModel: OtameshiViewModel = viewModel()
) {
Column(
modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val uiState by viewModel.uiState.collectAsState()
Button(
onClick = { viewModel.changeButtonText()},
modifier= Modifier.size(width= 200.dp, height = 50.dp)
) {
Text(text = uiState.buttonText,fontSize=30.sp)
}
}
}
OtammeshiApp()のかっこの中に、
viewModel: OtameshiViewModel = viewModel()
を追加。
これで画面からOtameshiViewModelクラスが使えるようになった。
次に{}の一番上で、val uiState by viewModel.uiState.collectAsState()
を設定
これはOtameshiViewModelの中で、読み取り専用で作ったuiState変数のこと。
uiState.buttonTextにAとかBが入るので、ボタンのテキストにはuiState.buttonText
を表示するように設定。
ボタンをクリックしたときの処理onClick
はモデルのchangeButtonText
メソッドを設定。
最後に
画面が1個しかないなら、viewModelは正直面倒くさい!
作る意味もわからん
この記事を書いてしみじみ思った。
画面をたくさん作りたくなってからViewModelを勉強しても遅くないかなって。