115 votes

Notifier l'observateur lorsque l'élément est ajouté à la liste de LiveData

J'ai besoin d'obtenir un événement Observer lorsque l'élément est ajouté à la liste de LiveData. Mais à ma connaissance, l'événement n'est reçu que lorsque je remplace l'ancienne liste par une nouvelle. Par exemple, lorsque je fais ce qui suit :

list.value = mutableListOf(IssuePost(UserEntity(name, email, photoUrl), issueEntity))

L'Observer reçoit l'événement. Mais lorsque j'ajoute simplement un élément à la valeur, l'Observer reste silencieux. Pourriez-vous SVP me donner des conseils sur la façon dont je peux implémenter ce dont j'ai besoin?

3 votes

Cela fonctionne comme vous le dites. Vous pouvez créer une sous-classe de LiveData et la méthode "add" qui créerait une nouvelle liste à partir de l'ancienne liste et ajouterait un nouvel élément.

0 votes

@bongo Que veux-tu dire? Est-ce que cela fonctionne lorsque j'ajoute un élément à la liste?

0 votes

Je veux dire que cela fonctionne comme "Mais autant que je sache, l'événement se déclenche uniquement lorsque je remplace la vieille liste par une nouvelle".

108voto

Programmer001 Points 500

En interne, LiveData garde une trace de chaque changement sous la forme d'un numéro de version (un simple compteur stocké en tant qu'entier). L'appel à setValue() incrémente cette version et met à jour tous les observateurs avec les nouvelles données (uniquement si le numéro de version de l'observateur est inférieur à celui de LiveData).

Il semble que la seule façon de démarrer ce processus est d'appeler setValue() ou postValue(). L'effet secondaire est que si la structure de données sous-jacente de LiveData a changé (par exemple en ajoutant un élément à une Collection), rien ne sera fait pour communiquer cette modification aux observateurs.

Ainsi, vous devrez appeler setValue() après avoir ajouté un élément à votre liste. J'ai fourni ci-dessous deux façons dont vous pourriez aborder cela.

Option 1

Gardez la liste en dehors de LiveData et mettez à jour la référence à chaque fois que le contenu de la liste change.

private val mIssuePosts = ArrayList()
private val mIssuePostLiveData = MutableLiveData>()

fun addIssuePost(issuePost: IssuePost) {
   mIssuePosts.add(issuePost)
   mIssuePostLiveData.value = mIssuePosts
}

Option 2

Suivez la liste via LiveData et mettez à jour LiveData avec sa propre valeur chaque fois que le contenu de la liste change.

private val mIssuePostLiveData = MutableLiveData>()

init {
   mIssuePostLiveData.value = ArrayList()
}

fun addIssuePost(issuePost: IssuePost) {
    mIssuePostLiveData.value?.add(issuePost)
    mIssuePostLiveData.value = mIssuePostLiveData.value
}

L'une ou l'autre de ces solutions devrait vous éviter d'avoir à créer une nouvelle liste à chaque fois que vous modifiez la liste actuelle simplement pour avertir les observateurs.

MISE À JOUR :

J'utilise des techniques similaires depuis un certain temps maintenant, comme Gnzlt l'a mentionné dans sa réponse, en utilisant une extension de fonction Kotlin pour attribuer le LiveData à lui-même afin de simplifier le code. C'est essentiellement l'Option 2 automatisée :) Je vous recommande de le faire.

0 votes

Suis-je correct en disant que dans ce cas, l'observateur recevra une nouvelle liste à chaque fois ? Est-il possible de ne souscrire qu'aux nouveaux éléments ajoutés à la liste ?

104voto

Gnzlt Points 613

J'utilise une Fonction d'Extension Kotlin pour faciliter les choses :

fun  MutableLiveData.notifyObserver() {
    this.value = this.value
}

Ensuite, utilisez-la dans n'importe quelle MutableLiveData comme ceci :

fun addIssuePost(issuePost: IssuePost) {
    mIssuePostLiveData.value?.add(issuePost)
    mIssuePostLiveData.notifyObserver()
}

23 votes

Meilleure façon fun MutableLiveData>.add(item: T) { val updatedItems = this.value as ArrayList updatedItems.add(item) this.value = updatedItems }

4 votes

@musooff, Dans mon cas, cela a provoqué un crash. C'est pourquoi j'ai modifié un peu votre code. J'ai utilisé MutableLiveData> au lieu de MutableLiveData>

3 votes

La solution originale de Gnzlt est la meilleure. Elle est plus universelle car elle sépare le type de données de la notification (séparation des préoccupations).

13voto

Ehsan Points 323

En retard à la fête mais voici une version plus concise de la réponse de Gnzlt avec vérification de null et pour les listes mutables et immutables :

// pour liste mutable
operator fun  MutableLiveData>.plusAssign(item: T) {
    val value = this.value ?: mutableListOf()
    value.add(item)
    this.value = value
}

// pour liste immuable
operator fun  MutableLiveData>.plusAssign(item: T) {
    val value = this.value ?: emptyList()
    this.value = value + listOf(item)
}

Et dans votre code :

list += IssuePost(UserEntity(name, email, photoUrl), issueEntity))

0 votes

Comment utiliseriez-vous ceci de manière générique ?

4voto

user3682351 Points 56

Que diriez-vous de ceci?

public class ListLiveData extends LiveData> {
    public void addAll(List items) {
        if (getValue() != null && items != null) {
            getValue().addAll(items);
            setValue(getValue());
        }
    }

    public void clear() {
        if (getValue() != null) {
            getValue().clear();
            setValue(getValue());
        }
    }

    @Override public void setValue(List value) {
        super.setValue(value);
    }

    @Nullable @Override public List getValue() {
        return super.getValue();
    }
}

// ajouter un auditeur de changement
    mMessageList.observe(mActivity, new Observer() {
        @Override public void onChanged(@Nullable Object o) {
            notifyDataSetChanged();
        }
    });

0voto

William Reed Points 762

Inspiré par @user3682351 voici ma solution. Il semble que nous ne pouvons pas mettre à jour les propriétés d'une valeur LiveData individuellement, donc la valeur doit être mise à jour à chaque opération. Il s'agit essentiellement d'un petit wrapper sur les données en direct avec des méthodes pratiques pour modifier les propriétés d'une HashMap

import androidx.lifecycle.LiveData

/**
 * Hash Map Live Data
 *
 * Quelques méthodes pratiques autour des données en direct pour les HashMaps. Mettre une valeur sur cela mettra également à jour l'ensemble des données en direct
 */
class HashMapLiveData : LiveData>() {

    /**
     * Mettre une nouvelle valeur dans cette HashMap et mettre à jour la valeur de ces données en direct
     * @param k la clé
     * @param v la valeur
     */
    fun put(k: K, v: V) {
        val oldData = value
        value = if (oldData == null) {
            hashMapOf(k to v)
        } else {
            oldData.put(k, v)
            oldData
        }
    }

    /**
     * Ajouter le contenu à la HashMap s'il en existe une existante, sinon définir la valeur sur cette HashMap et mettre à jour la
     * valeur de ces données en direct
     * @param newData la HashMap de valeurs à ajouter
     */
    fun putAll(newData: HashMap) {
        val oldData = value
        value = if (oldData != null) {
            oldData.putAll(newData)
            oldData
        } else {
            newData
        }
    }

    /**
     * Supprimer une paire clé-valeur de cette HashMap et mettre à jour la valeur de ces données en direct
     * @param key la clé à supprimer
     */
    fun remove(key: K) {
        val oldData = value
        if (oldData != null) {
            oldData.remove(key)
            value = oldData
        }
    }

    /**
     * Effacer toutes les données de la HashMap de base et mettre à jour la valeur de ces données en direct
     */
    fun clear() {
        val oldData = value
        if (oldData != null) {
            oldData.clear()
            value = oldData
        }
    }

    var value: HashMap?
        set(value) = super.setValue(value)
        get() = super.getValue()

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X