0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ViewModelのことがわかってきたので、忘れないよう記録しておく

クリックするたびボタンのテキストがA→B→Aのように変わるボタンを実装

ViewModelを使わない場合

MainActivityクラスの下にOtameshiApp関数を追加。
(既存のGreetingなんちゃらの中に記述してもいいけど!)

Column設定して(面倒ならmodifierはいらない)、ボタンを1つ設定。

ボタンのテキスト用の変数buttonTextを宣言。
ボタンのonClickに直接ボタンのテキストを変更する処理を記述

MainActivity.kt
@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とボタンのテキストが変わる。
image.png

ボタンをBにしたまま、画面を回転させると・・・
image.png
Aに戻る不思議:sweat_smile:

この問題は変数宣言時に'remember'ではなく
var buttonText by rememberSaveable { mutableStateOf("A") }とすると解決するが、根本解決にはなってない。
なぜならこの画面でボタンをBにしてから、別の画面に移動し、再度この画面に戻ってきたとき、Aボタンに戻ってしまうから。
(別の画面肉と値が保持されないので)

他にも、同じボタンをこの画面と別画面で共有したくても、画面ごとでしか状態が保持できないので一貫性を保つのが難しい。

あと、テストでもremember {mutableStateOf("A"}を直接使うことができないので、状態のテストがやりづらい。

これらの問題がViewModelを使うと全部解決できてしまう!:sparkles:

ViewModelを使う場合のコード例

1.gradleファイルに依存関係を追加

なんか、implementationの書き方が変わった。
簡単になった!°˖☆◝(⁰▿⁰)◜☆˖°

build.gradle.kts(Module:app)
dependencies {
    //割愛
    implementation(libs.lifecycle.viewmodel.compose)
    //割愛
}

追加したら、SyncNowする。
ほんと、よく忘れるので、やる。絶対。

2.画面と分離させるため、新しいKotolinファイルを作る

model用のパッケージ作って、その中にファイル作るのが正しいと思うけど、テスト的に作ってるアプリなので、いったん別ファイルにしておけばOKということで!
image.png

3.状態を管理するクラスを作る

1個しか管理する項目がないのに作るんかーい!って気持ちになったけど、増えるかもしれないから。

OtameshiViewModel.kt
data class OtameshiUiState(
    val buttonText:String = "A"
)

4.ViewModelのクラスを作る

とりあえず、おまじないのように以下を記述

OtameshiViewModel.kt
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とアクセスできるよう変更していく。

MainActivity.kt
@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は正直面倒くさい!
作る意味もわからん:ghost:
この記事を書いてしみじみ思った。
画面をたくさん作りたくなってからViewModelを勉強しても遅くないかなって。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?