Quelle est la bonne façon de communiquer entre le ViewModel
et le View
, Google architecture components
donner une utilisation LiveData
dans laquelle la vue souscrit aux modifications et se met à jour en conséquence, mais cette communication ne convient pas pour des événements uniques, par exemple afficher le message, afficher la progression, masquer la progression, etc.
Il existe des hacks comme SingleLiveEvent
dans l'exemple de Google, mais cela ne fonctionne que pour 1 observateur. Certains développeurs utilisent EventBus
mais je pense que cela peut rapidement devenir hors de contrôle lorsque le projet se développe.
Existe-t-il un moyen pratique et correct de le mettre en œuvre, comment le mettez-vous en œuvre?
(les exemples Java sont également les bienvenus)
Pour afficher/masquer les boîtes de dialogue de progression et afficher les messages d'erreur d'un appel réseau ayant échoué lors du chargement de l'écran, vous pouvez utiliser un wrapper qui encapsule les LiveData observées par la vue.
Les détails de cette méthode se trouvent dans l'addendum à l'architecture de l'application: https://developer.Android.com/jetpack/docs/guide#addendum
Définissez une ressource:
data class Resource<out T> constructor(
val state: ResourceState,
val data: T? = null,
val message: String? = null
)
Et un ResourceState:
sealed class ResourceState {
object LOADING : ResourceState()
object SUCCESS : ResourceState()
object ERROR : ResourceState()
}
Dans le ViewModel, définissez vos LiveData avec le modèle enveloppé dans une ressource:
val exampleLiveData = MutableLiveData<Resource<ExampleModel>>()
Toujours dans le ViewModel, définissez la méthode qui fait l'appel API pour charger les données pour l'écran actuel:
fun loadDataForView() = compositeDisposable.add(
exampleUseCase.exampleApiCall()
.doOnSubscribe {
exampleLiveData.setLoading()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
exampleLiveData.setSuccess(it)
},
{
exampleLiveData.setError(it.message)
}
)
)
Dans la vue, configurez l'observateur lors de la création:
viewModel.exampleLiveData.observe(this, Observer {
updateResponse(it)
})
Voici l'exemple de la méthode updateResponse()
, montrant/masquant la progression et affichant une erreur le cas échéant:
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
}
}
}
}
essaye ça:
/**
* 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
}
Et enveloppez-le dans 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
}
}
Et observez
myViewModel.navigateToDetails.observe(this, Observer {
it.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled
startActivity(DetailsActivity...)
}
})
référence du lien: tiliser un wrapper d'événement