3
2

More than 3 years have passed since last update.

【Android】ViewModel内でUiModelを使おう(その1)[sealed class編]

Last updated at Posted at 2021-03-16

はじめに

ViewModelは現在多くのプロジェクトで利用されているかと思いますが、LiveDataが多くなってくると管理するのが大変になってきます。最終的にはUiModelを使ってLiveDataの数を少なくし、ViewModelに関するコードをすっきりさせるのが目的です。今回は最初の段階としてsealed classを使ってViewModelをきれいに書く方法です。

対象

  • ViewModelを利用している方(MVVMのViewModelというよりはAndroidのViewModelライブラリ)
  • kotlinが読める方

LiveDataの型をどうしていますか?

例えば画面に表示する文字列をViewModelで管理する場合、下記のようにLiveDataを用意します。

MyViewModel
class MyViewModel : ViewModel() {
    private val _text = MutableLiveData<String>("") // 初期化方法は導入しているライブラリによっても違うのでお好きな方法で
    val text: LiveData<String> = _text
    
}

開発中や開発初期の場合はこれでも良いですが、あとから「文字列はサーバから通信して取得したいな」とか「文字列を使用したら空文字にしたりデフォルト値にしたい」とかいろんな要望があとから出てくることもあると思います。今回は通信中を表すisLoadingを下記のように実装します。

MyViewModel
class MyViewModel : ViewModel() {
    private val _text = MutableLiveData<String>("")
    val text: LiveData<String> = _test
    private val _isLoading = MutableLiveData<Boolean>(false)
    val isLoading: LiveData<Boolean> = _isLoading
    
}

LiveDataに状態を持たせる

まだLiveDataは2つなので、そんなに問題にはなりませんが、GUI上で表示したいデータや扱いたいデータが多くなってくるとLiveDataの数がどんどん増えてしまいます。このような場合は新しくsealed classobjectdata classを利用してすっきり書けるようになります。下記のようなクラスを用意します。

TextState
sealed class TextState {
    data class Loaded(val text: String) : TextState
    object Loading : TextState
}

ViewModelをすっきりさせる

TextStateを作ったことにより、MyViewModelは下記のように書き直すことができます。

MyViweModel
class MyViewModel : ViewModel() {
    private val _textState = MutableLiveData<TextState>(TextState.Loading)
    val textState: LiveData<TextState> = _textState
    
}

このように実装することでLiveDataの数が一つ減って文字列と状態を1つのLiveDataで管理できるようになったので、すっきりしました。あとは通信中の場合_textState.postValue(TextState.Loading)、通信完了時は_textState.postValue(TextState.Loaded("complete"))とするとLiveDataを観測する側は通信中、通信完了及び取得した文字列を扱えるようになりました。

FragmentやActivity等
class MyFragment : Fragment() {
    override fun onViewCreated((view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        myViewModel.textState.observe(viewLifecycleOwner) { textState ->
            textView.text = when (textState) {
                is TextState.Loading -> "loading"
                is TextState.Loaded -> textState.text
                // sealed classなのでelseを書く必要がありません
            }
        }
        /*
        これが不要になりました。
        myViewModel.isLoading.observe(viewLifecycleOwner) {
        }
        */
    }
    
}

まとめ

LiveData内の変数の型をこのように変更するとViewModel内のコードが短くなり、それを観測する側のFragmentやActivityもLiveDataの数だけobserveする必要がなくなったので、見通しが良くなったのではないでしょうか?もし通信に失敗した場合の状態も追加したいのであればTextStateにobject LoadError : TextStateとかを追加すればコードの変更量を少なく、エラー表示にも対応でき、変更に強いViewModelを作ることが可能です。

次回 「【Android】ViewModel内でUiModelを使おう(その2)[UiModel編]

3
2
1

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
3
2