web-dev-qa-db-ja.com

LiveDataを使用したMVVMのビューとViewModel間の通信

ViewModelViewGoogle architecture componentsLiveDataを使用してビューを変更にサブスクライブし、それに応じて自身を更新する適切な方法は何ですか。ただし、この通信は、メッセージの表示、進行状況の表示、進行状況の非表示など、単一のイベントには適していません。

Googleの例ではSingleLiveEventのようなハックがいくつかありますが、それは1人のオブザーバーに対してのみ機能します。一部の開発者はEventBusを使用していますが、プロジェクトが大きくなるとすぐに制御不能になる可能性があると思います。

それを実装する便利で正しい方法はありますか?どのように実装しますか?

(Javaの例も歓迎)

5
Pavel Poley

進行状況ダイアログの表示/非表示、および画面のロード時に失敗したネットワーク呼び出しからのエラーメッセージを表示するには、ビューが監視しているLiveDataをカプセル化するラッパーを使用できます。

このメソッドの詳細は、アプリアーキテクチャの付録に記載されています。 https://developer.Android.com/jetpack/docs/guide#addendum

リソースを定義します。

_data class Resource<out T> constructor(
        val state: ResourceState,
        val data: T? = null,
        val message: String? = null
)
_

そして、ResourceState:

_sealed class ResourceState {
    object LOADING : ResourceState()
    object SUCCESS : ResourceState()
    object ERROR : ResourceState()
}
_

ViewModelで、リソースにラップされたモデルでLiveDataを定義します。

_val exampleLiveData = MutableLiveData<Resource<ExampleModel>>()
_

また、ViewModelで、現在の画面のデータをロードするためのAPI呼び出しを行うメソッドを定義します。

_fun loadDataForView() = compositeDisposable.add(
        exampleUseCase.exampleApiCall()
                .doOnSubscribe {
                    exampleLiveData.setLoading()
                }
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        {
                            exampleLiveData.setSuccess(it)
                        },
                        {
                            exampleLiveData.setError(it.message)
                        }
                )
)
_

ビューで、作成時にオブザーバーを設定します。

_    viewModel.exampleLiveData.observe(this, Observer {
        updateResponse(it)
    })
_

次に、updateResponse()メソッドの例を示します。進行状況を表示/非表示にし、必要に応じてエラーを表示します。

_private fun updateResponse(resource: Resource<ExampleModel>?) {
    resource?.let {
        when (it.state) {
            ResourceState.LOADING -> {
                showProgress()
            }
            ResourceState.SUCCESS -> {
                hideProgress()
                // Use data to populate data on screen
                // it.data will have the data of type ExampleModel

            }
            ResourceState.ERROR -> {
                hideProgress()
                // Show error message
                // it.message will have the error message
            }
        }
    }
}
_
0
Daniel Nugent

これを試して:

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

そしてそれをLiveDataにラッパーします

class ListViewModel : ViewModel {
    private val _navigateToDetails = MutableLiveData<Event<String>>()

    val navigateToDetails : LiveData<Event<String>>
        get() = _navigateToDetails


    fun userClicksOnButton(itemId: String) {
        _navigateToDetails.value = Event(itemId)  // Trigger the event by setting a new Event as a new value
    }
}

そして観察する

myViewModel.navigateToDetails.observe(this, Observer {
    it.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled
        startActivity(DetailsActivity...)
    }
})

リンク参照: イベントラッパーを使用

0
Tuan Dao