背景
前回jetpack composeでカウントアップアプリを作成したときに、クリック回数を記憶する変数をActivityに直書きしていました。
↓前回製作したもの
ただ、画面の回転やライフサイクルの働きによりactivity内の一時的に保存している情報が破棄されてしまい、リセットされる危険性があるためViewModelにカウントアップの処理を移動させました。
今回の変更
クリック回数計算をviewModelに託す
Before
var countTapStar by remember {
mutableStateOf(0)
}
var countTapTriangle by remember {
mutableStateOf(0)
}
After
val countTapStar by countViewModel.starCount.observeAsState(initial = 0)
val countTapTriangle by countViewModel.triangleCount.observeAsState(initial = 0)
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の全体像
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() })
}
}
}
実際の動き
よくなった点
画面回転やライフサイクルによるデータリセット対策ができました。 背景でも記載した通り、スマホには外的要因によってデータが破棄される危険性を孕んでいます。その対策として画面回転などをしても破棄されないviewModelに値を記憶しておくことで、アプリの安全性を高めることができました。
もう一つは役割分担することでプログラムの可読性が上がりました。 画面宣言しているパートとロジックパートが一緒になっているプログラムは、読みづらいうえに些細な変更に対して弱いプログラムになってしまいます。
上司から「このプログラムに既存の機能に影響がないように機能を追加してくれ」と頼まれふたを開けてみたら、たくさんのメソッドがあるうえにこうしたコードだったら気絶するでしょう。
まだアーキテクチャについては学習中ですが、学習する前と後ではコーディングの正確性がまるで違うことを実感しています。
参考サイト
Repository