【Android】Jetpack ComposeでViewModelを入れてみた

## 背景
前回jetpack composeでカウントアップアプリを作成したときに、クリック回数を記憶する変数をActivityに直書きしていました。
↓前回製作したもの

https://github.com/jtakumi/jetpackComposebasic/tree/clickableImage

ただ、画面の回転やライフサイクルの働きによりactivity内の一時的に保存している情報が破棄されてしまい、リセットされる危険性があるためViewModelにカウントアップの処理を移動させました。

## 今回の変更
クリック回数計算をviewModelに託す

### Before
```MainActivity.kt
  var countTapStar by remember {
            mutableStateOf(0)
        }
        var countTapTriangle by remember {
            mutableStateOf(0)
        }
```

### After
```MainActivity.kt
    val countTapStar by countViewModel.starCount.observeAsState(initial = 0)
    val countTapTriangle by countViewModel.triangleCount.observeAsState(initial = 0)
     
```
```CountViewModel.kt

class CountViewModel : ViewModel() {
    //MutableLiveDataで監視と変更ができる値を宣言する
    val starCount = MutableLiveData(0)
    val triangleCount = MutableLiveData(0)
    //宣言した変数の値を読み込んで上書きする
    fun starCountUp() {
        val starPlusCount = starCount.value ?: 0
        starCount.value = starCount.value?.plus(1)
    }

    fun triangleCountUp() {
        val trianglePlusCount = triangleCount.value ?: 0
        triangleCount.value = triangleCount.value?.plus(1)
    }
}

```

## MainActivity.ktの全体像


```MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Column {
                showImage()
            }
        }
    }
}

@Composable
fun showImage(
    countViewModel: CountViewModel = viewModel()
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.SpaceEvenly,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        //viewModelの変数を結びつけながら初期値設定する
        val countTapStar by countViewModel.starCount.observeAsState(initial = 0)
        val countTapTriangle by countViewModel.triangleCount.observeAsState(initial = 0)
        Row {
            Text(
                text = "Star:$countTapStar ,",
                fontSize = 16.sp,
                color = colorResource(id = R.color.cherry_rose)
            )
            Text(
                text = "Triangle:$countTapTriangle",
                fontSize = 16.sp,
                color = colorResource(id = R.color.moegi)
            )
        }
        Row {
            Image(
                painter = painterResource(id = R.drawable.icon_svg), contentDescription = "",
                modifier = Modifier
                    .size(108.dp)
                    .border(1.dp, color = colorResource(id = R.color.moegi))
                    //Before
                    // .clickable { countTapStar++ },
                    .clickable { countViewModel.starCountUp() },
                contentScale = ContentScale.Crop,
            )
            Image(
                painter = painterResource(id = R.drawable.triangle),
                contentDescription = "",
                contentScale = ContentScale.Crop,
                modifier = Modifier
                    .size(108.dp)
                    //Before
                    // .clickable { countTapTriangle++ })
                    .clickable { countViewModel.triangleCountUp() })
        }
    }
}
```

## 実際の動き
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747809/c03cf499-7ee8-eac3-d2c1-23283d11f882.gif width=300>

## よくなった点

画面回転やライフサイクルによる**データリセット対策ができました。** 背景でも記載した通り、スマホには外的要因によってデータが破棄される危険性を孕んでいます。その対策として画面回転などをしても破棄されないviewModelに値を記憶しておくことで、アプリの安全性を高めることができました。

もう一つは役割分担することで**プログラムの可読性が上がりました。** 画面宣言しているパートとロジックパートが一緒になっているプログラムは、読みづらいうえに些細な変更に対して弱いプログラムになってしまいます。
上司から「このプログラムに既存の機能に影響がないように機能を追加してくれ」と頼まれふたを開けてみたら、たくさんのメソッドがあるうえにこうしたコードだったら気絶するでしょう。

まだアーキテクチャについては学習中ですが、**学習する前と後ではコーディングの正確性がまるで違うことを実感しています。**

## 参考サイト

https://developer.android.com/codelabs/basic-android-kotlin-training-viewmodel?hl=ja#0

https://engawapg.net/jetpack-compose/1426/work-with-viewmodel/

https://developer.android.com/reference/kotlin/androidx/lifecycle/MutableLiveData

## Repository

https://github.com/jtakumi/jetpackComposebasic/tree/viewModel