Androidアプリ開発初級編(ViewModel+LiveData+DataBinding)
今回はAACのViewModel、LiveDataを使った簡単なアプリを使って使い方について学習したいと思います。
■ViewModelとは
ViewModelとは Android Archtecture Components の一つで、UIデータの保持ができ onSaveInstanceStateよりも便利で使いやすいです。またLiveDataやDataBindingを組み合わせることでよりコードの複雑さを解消できUI周りのモジュール間の関心の分離に対して効果を発揮します。ライフサイクルと適応しているため、手動でのライフサイクル管理が不要。
■LiveDataとは
LiveDataとは Android Archtecture Components の一つで、データの観測を目的とし、値の変化を検知して何かを処理する際に効果を発揮します。ライフサイクルと適応しているため、手動でのライフサイクル管理が不要。
■事前準備
前回のNavigationサンプルアプリをそのまま使ってViewModelを追加していきます。
■サンプルアプリの動作
0~100の数字を2つ選択して、計算を行う
■ViewModelの作成
FragmentCに対して下記のViewModelを作成します。
計算用のIntを二つと、計算結果のテキスト、除算判定用のBooleanを管理しています。
Udacityでも取り上げられていましたが、必ずカプセル化を意識して値を管理してください。決して直接FragmentやActivityから値を操作しないように。
class CalcDivisionTimesModel(val firstValue: LiveData<Int>, val secondValue: LiveData<Int>) : ViewModel() { private var _calcResultText = MutableLiveData<String>() val calcResultText: LiveData<String> get() = _calcResultText private var _isCalcDivision = MutableLiveData<Boolean>() val isCalcDivision: LiveData<Boolean> get() = _isCalcDivision init { _calcResultText.value = (firstValue.value!!.times(secondValue.value!!)).toString() _isCalcDivision.value = false } fun onClickDivision() { _calcResultText.value = (firstValue.value!!.div(secondValue.value!!.toFloat())).toString() _isCalcDivision.value = true } fun onClickPow() { _calcResultText.value = (firstValue.value!!.times(secondValue.value!!)).toString() _isCalcDivision.value = false } }
onClickDivisionやonClickPowはDataBindingを用いて紐づけを行います。
<data> <variable name="calcViewModel" type="com.tomostudy.navigationtest.minus.CalcDivisionTimesModel" /> </data> .... <Button android:id="@+id/pow_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="52dp" android:layout_marginEnd="24dp" android:onClick="@{() -> calcViewModel.onClickPow()}" android:text="@string/pow" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/division_button" app:layout_constraintTop_toBottomOf="@+id/result_text" />
またLiveDataを使うことにより、データをバインドするだけで値が変更した際にUIの更新まで行うことが可能です。
<TextView android:id="@+id/result_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{calcViewModel.calcResultText}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/calcSignText" />
ViewModelに引数を渡しているので、今回はViewModelFactoryを作成していきます。ViewModelProvider.NewInstanceFactoryを使って作成。
class MinusViewModelFactory(private val firstValue: Int, private val secondValue: Int) : ViewModelProvider.NewInstanceFactory() { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return CalcDivisionTimesModel( MutableLiveData(firstValue), MutableLiveData(secondValue) ) as T } }
■Fragment側の実装
次にFragment側の実装を見ていきましょう
bindingオブジェクトのインスタンスにviewModelを代入することでデータバインディングを設定。bindingにライフサイクルを設定することでLiveDataのライフサイクルが管理されるようになります。これをしないとLiveDataを元にUIが更新されないので注意してください。
次にViewModel内のLiveDataに対してオブザーバーを設定し、値の変更時にフラグメント側でリソースを更新しています。
このようにViewModel+LiveData+DataBindingにより従来よりも遥かにコードがすっきりしています。
class FragmentC : Fragment() { private val viewModel: CalcDivisionTimesModel by viewModels { MinusViewModelFactory( args.firstValue, args.secondValue ) } private val args: FragmentCArgs by navArgs() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val binding: FragmentCBinding = DataBindingUtil.inflate( inflater, R.layout.fragment_c, container, false ) binding.calcViewModel = viewModel binding.lifecycleOwner = viewLifecycleOwner binding.calcViewModel!!.isCalcDivision.observe( viewLifecycleOwner, Observer { isCalcDivision -> if (isCalcDivision) { binding.calcSignText.text = getString(R.string.division_sign) } else { binding.calcSignText.text = getString(R.string.pow_sign) } }) return binding.root } }
■まとめ
・ViewModelによりUIデータを保持することが出来る
・LiveDataによりデータを観測して処理を制御出来る
・これらと共にDataBindingを組み合わせることでActivity/Fragmentの肥大化を防ぎ、同時にモジュール間の関心を分離することが出来る。
・ライフサイクル管理を手動で制御せずに済む
アプリ開発を始めたばかりで、アーキテクチャを知らなくてもアプリ構成がMVVMになるはずなのでコードが強制的にすっきりするのが本当に良いですよね。他にもライフサイクル管理という面倒で複雑化の要素になっていたものが解決されているのもすごい。是非これらを活用してアプリ開発をより良くしていきましょう。
それでは。