9
votes

Comment émettre des valeurs distinctes à partir de MutableLiveData?

J'ai observé que MutableLiveData déclenche onChanged d'un observateur même si la même instance d'objet est fournie à sa méthode setValue .

class DistinctLiveData<T> : MutableLiveData<T>() {

    private var cached: T? = null

    @Synchronized override fun setValue(value: T) {
        if(value != cached) {
            cached = value
            super.setValue(value)
        }
    }

    @Synchronized override fun postValue(value: T) {
        if(value != cached) {
            cached = value
            super.postValue(value)
        }
    }
}

Existe-t-il un moyen d'éviter qu'un observateur ne soit notifié deux fois si la même instance ou une instance équivalente est fournie à setValue () / postValue ()

J'ai essayé d'étendre MutableLiveData mais cela n'a pas fonctionné. Il se peut que je manque quelque chose ici

//Fragment#onCreateView - scenario1
val newValue = "newValue"
mutableLiveData.setValue(newValue) //triggers observer
mutableLiveData.setValue(newValue) //triggers observer

//Fragment#onCreateView - scenario2
val newValue = "newValue"
mutableLiveData.postValue(newValue) //triggers observer
mutableLiveData.postValue(newValue) //does not trigger observer


0 commentaires

4 Réponses :


11
votes

Vous pouvez utiliser le tour de magie suivant pour consommer "les éléments étant identiques":

fun <T> LiveData<T>.distinctUntilChanged(): LiveData<T> = MediatorLiveData<T>().also { mediator ->
    mediator.addSource(this, object : Observer<T> {
        private var isInitialized = false
        private var previousValue: T? = null

        override fun onChanged(newValue: T?) {
            val wasInitialized = isInitialized
            if (!isInitialized) {
                isInitialized = true
            }
            if(!wasInitialized || newValue != previousValue) {
                previousValue = newValue
                mediator.postValue(newValue)
            }
        }
    })
}

Si vous voulez vérifier l'égalité référentielle, c'est ! == .


0 commentaires

27
votes

Il existe déjà dans l'API: Transformations.distinctUntilChanged ()

public static LiveData<X> distinctUntilChanged (LiveData<X> source)

Crée un nouvel objet LiveData n'émet pas de valeur tant que la source La valeur LiveData a été modifiée. La valeur est considérée comme modifiée si equals () donne false .

>


3 commentaires

Étant donné que cela a été signalé comme une réponse possible avec lien uniquement, j'ai extrait un peu de la documentation.


Il convient de mentionner que Transformations.distingUntilChanged () est disponible au plus tôt dans la version 2.1.0 qui n'est pas encore une version stable! developer.android.com/jetpack/androidx/releases/… < / a>


mise à jour: il est maintenant dans la version stable. sûr à utiliser.



2
votes

Si nous parlons de MutableLiveData , vous pouvez créer une classe et remplacer setValue, puis n'appeler via super que si nouvelle valeur! = ancienne valeur

class DistinctUntilChangedMutableLiveData<T> : MutableLiveData<T>() {
    override fun setValue(value: T?) {
        if (value != this.value) {
            super.setValue(value)
        }
    }
}


4 commentaires

Je ne sais pas pourquoi cela n'a pas obtenu plus de votes positifs, semble le moyen le plus simple et le plus propre à imo.


Oui, vous devez savoir comment fonctionne l'égalité, mais ne vous trompez pas.


@ bastami82: Veuillez arrêter de changer ma réponse. Corrigez mes fautes de frappe, ajoutez un peu de mise en forme, c'est bien, pour toute autre chose, écrivez votre propre réponse.


j'ai juste essayé d'être utile car mes modifications suggérées n'ont PAS changé l'essence de votre réponse, j'aurais pu écrire ma propre réponse, vous avez raison, mais je pensais que, compte tenu de votre réponse, malheureusement, vous n'avez pas apprécié l'effort (au fait c'est ainsi que le code évolue). À mon avis, les changements suggérés l'ont rendu plus clair, je pensais seulement que c'était une bonne réponse et qu'il fallait un peu de polissage, déçu que vous ne l'aimiez pas mais je respecte votre décision.



0
votes

Dans mon cas, j'ai des objets assez complexes que je dois comparer par certains champs. Pour cela, j'ai changé la réponse d'EpicPandaForce:

fun <T> LiveData<T>.distinctUntilChanged(compare: T?.(T?) -> Boolean = { this == it }): LiveData<T> = MediatorLiveData<T>().also { mediator ->
    mediator.addSource(this) { newValue ->
        if(!newValue.compare(value)) {
            mediator.postValue(newValue)
        }
    }
}

Par défaut, il utilise la méthode standard equals , mais si vous avez besoin - vous pouvez changer la logique de distinction


0 commentaires