35
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MediatorLiveData で DataBinding を少し楽にする

Last updated at Posted at 2018-12-09

LiveDataを~Bindingに直接Bindできるようになり、色々と開発が捗るようになりました。
しかし、素直にLiveDataを利用するだけだと
「あれ?これもうちょっときれいにかけそうな気がするんだけど、どう書けばいいんだろう・・・」
みたいな問題にぶつかることがあります。

この記事ではそんな時に思い出してほしいMediatorLiveDataについて簡単に説明します。

たまにぶつかる問題点

  • LiveData A と LiveData B の値から、LiveData C の値を作りたい
  • AかBのどちらかが変更されたらCもそのタイミングで変更したい

具体的な例

  • EditText A(prefixText) と EditText B (mainText) を準備
  • TextView C(combinedText) は、"prefixText" + "mainText" の文字列を表示する
  • prefixText または mainText の文字列が変更されたら combinedText に表示する文字列も変更する

layout

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity"
            >

        <EditText
                android:id="@+id/prefixText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@={viewModel.prefixText}"
                app:layout_constraintTop_toTopOf="parent"
                tools:text="prefix text"
                />

        <EditText
                android:id="@+id/mainText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@={viewModel.mainText}"
                app:layout_constraintTop_toBottomOf="@+id/prefixText"
                tools:text="main text"
                />

        <TextView
                android:id="@+id/combinedText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{viewModel.combinedText}"
                android:layout_marginTop="50dp"
                android:textSize="30sp"
                app:layout_constraintTop_toBottomOf="@+id/mainText"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                tools:text="combined text"
                />
    </androidx.constraintlayout.widget.ConstraintLayout>
class MainActivityViewModel {
    val prefixText = MutableLiveData<String>().apply { value = "" }
    val mainText = MutableLiveData<String>().apply { value = "" }
    val combinedText = MutableLiveData<String>().apply { value = "" }
}

素直に書いた例

ViewModelを設定しているActivityのonCreateなどで下記のコードを書く

val textObserver = Observer<String> {
    val prefix = viewModel.prefixText.value ?: ""
    val main = viewModel.mainText.value ?: ""
    viewModel.combinedText.value = "$prefix-$main"
}
viewModel.prefixText.observe(this, textObserver)
viewModel.mainText.observe(this, textObserver)

prefixText, mainText のどちらを変更しても combinedText が変更されて、問題なく動作することが確認できます。
しかし、Activity側でLiveDataをobserveして別のLiveDataに値をセットする、というのはなんとなく気になるところです。

combinedText 自体に、prefixText, mainText を監視して自分自身の値を変更する機能があればいいのに・・・

ご安心ください。
そんな機能が用意されています。
それが今回ご紹介するMediatorLiveDataです。

MediatorLiveDataを利用した例

ActivityのonCreateなどに書いていたコードを削除する
ViewModelを次のように書き換える

class MainActivityViewModel {
    val prefixText = MutableLiveData<String>().apply { value = "" }
    val mainText = MutableLiveData<String>().apply { value = "" }
    val combinedText = MediatorLiveData<String>()

    init {
        val textObserver = Observer<String> {
            val prefix = prefixText.value ?: ""
            val main = mainText.value ?: ""
            combinedText.value = "$prefix-$main"
        }
        // prefixText が変更されたら、textObserver の処理が実行されるように設定する
        combinedText.addSource(prefixText, textObserver)
        // mainText が変更されたら、textObserver の処理が実行されるように設定する
        combinedText.addSource(mainText, textObserver)
    }
}

Activityに書いてあった処理がなくなり、ViewModel内でやりたいことが完結するようになりました。
Bindのためのコードも、自動生成されるActivityMainBindingからだけになり、バグも減らせそうでいい感じです。

まとめ

  • MediatorLiveDataを利用すると、他のLiveDataの変更のタイミングで自分自身の値の変更を行うことができる
  • DataBindingと合わせて利用した場合、Bindのためのコードが自動生成されるクラスからのみになるため見通しがよくなる
  • これ、RxでいうcombineLatestでは・・・?

おまけ(確認に使ったサンプルアプリの動き)

MediatorLiveDataSample.gif

35
29
0

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
35
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?