4月からAndroid開発を行うことになったので頭の整理として記事にしていきます。
今回はViewmodelです。
ViewModel
Androidの初心者本をやっているとActivityで何でも行っていたので
実務を始めてからViewmodelの存在は知りました。
公式ドキュメントでは
ViewModel は、ライフサイクルを意識した方法で UI 関連のデータを保存し管理するためのクラスです。ViewModel クラスを使用すると、画面の回転などの構成の変更後にデータを引き継ぐことができます。
とあります。どうやらActivityにデータを持つと画面回転しただけで消えてしまう様です。
検証
簡単なサンプルを作成して実際に検証しました。
MainActivity
class MainActivity : AppCompatActivity() {
// カウント値
private var countValue = 0
private lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById<TextView>(R.id.count_result)
//初期値
textView.text = countValue.toString()
//カウント処理
findViewById<Button>(R.id.count_button).setOnClickListener{
countValue++
textView.text = countValue.toString()
}
}
}
仕様はCOUTN UPボタンを押下すると、その下にカウント数が表示されるシンプルなもので、
ボタン押下後、画面を回転させました。
結果はこのように画面を回転後はカウントアップの数字が消えてしまい、データが引き継がれていないことが確認できました。
原因はドキュメントに記載されています。Activityのライフサイクルに起因するとのこと。
システムによって UI コントローラが破棄または再作成されると、UI コントローラに保存されている一時的な UI 関連のデータはすべて失われます。
ViewModelを使ってみる
それではViewModelを使ってActivityが破棄されてもデータが失われないようにしたいと思います。
まずViewModelクラスを継承したクラスを作成します。
今回はカウント値を持つだけなのでシンプルですが下準備はこれだけです、
MainViewModel
import androidx.lifecycle.ViewModel
class MainViewModel: ViewModel() {
var countValue = 0
}
続いてMainActivityにViewModelを組み込んでいきます。
ViewModelの作成方法は色々ありますが、今回はViewModelProviderを利用しています。
簡単に説明すると引数にthis(MainActivity自身)を渡すことでviewmodelと関連付けています。
そして、画面に表示する値は宣言したviewModelプロパティから取得します。
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var textView: TextView
//viewModelを宣言しておく
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// viewModelを作成
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
textView = findViewById<TextView>(R.id.count_result)
// 値はviewModelを参照する
textView.text = viewModel.countValue.toString()
findViewById<Button>(R.id.count_button).setOnClickListener{
viewModel.countValue++
// 値はviewModelを参照する
textView.text = viewModel.countValue.toString()
}
}
}
たったこれだけですが結果は...
無事にカウント値を引き継ぐことができました。
Viewmodelを使わない場合
ちなみに少量のデータであればViewModelを使わなくてもデータの引き継ぎはできる様です。
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var textView: TextView
private var countValue = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById<TextView>(R.id.count_result)
textView.text = countValue.toString()
findViewById<Button>(R.id.count_button).setOnClickListener{
countValue++
textView.text = countValue.toString()
}
}
// onCreateのパラメータであるsavedInstanceStateがnullでなければ呼ばれる
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
// キーを指定して値を取り出し
var text = savedInstanceState?.getString("COUNT_VALUE")
textView.text = text
}
// ActivityのonStopとonDestoryの間で呼ばれる
override fun onSaveInstanceState(outState: Bundle) {
outState.run {
// 引き継がせたいデータをkeyValueで登録しておく
putString("COUNT_VALUE", countValue.toString())
}
super.onSaveInstanceState(outState)
}
}
まとめ
ViewModelの基本的な動きを学ぶことができました。