Comment faire LiveData<MutableList<T>> mise à jour lorsque je modifie une propriété de T?

0

La question

Je suis en train de faire une appli qui obtient la (pseudo) valeurs de latence en faisant une demande à certaines url et l'enregistrement de combien de temps cela va prendre.

Tout d'abord, j'utilise de rénovation pour obtenir une réponse JSON à partir d'un serveur web. Cette réponse contient: le nom de l'hôte (par exemple Ebay royaume-UNI), l'url de l'hôte (par ex. www.ebay.co.uk), et une url de l'image. J'ai une carte de cette réponse sur ma classe de données qui se présente comme suit:

data class(
    val name: String,
    var url: String,
    val icon: String,
    var averagePing: Long = -1
)

l'url est une var propriété avant de faire les appels pour obtenir les valeurs de latence, j'ai besoin d'ajouter https:// dans le but de faire la demande.

Je suis en train de faire tout cela de la sorte:

fun getHostsLiveData() {
    viewModelScope.launch(Dispatchers.IO) {
        val hostList = repo.getHosts()
        for (host in hostList) {
            host.url = "https://" + host.url
            host.averagePing = -1
        }
        hostListLiveData.postValue(hostList)//updated the recyclerview with initial values
        //with default (-1) value of averagePing

        for (host in hostList) {
            async { pingHostAndUpdate(host.url, hostList) }
        }
    }
}

La première boucle for prépare mes données. La ligne après la boucle for soumet les données pour le recycleur adaptateur, afin d'afficher le nom d'hôte, l'adresse url et l'icône tout de suite (ça fonctionne c'est à dire que j'ai un travail d'observateur pour la LiveData), alors que je suis en attente pour les valeurs de latence.

La deuxième boucle for appelle la fonction pour calculer les valeurs de latence pour chaque hôte et le updateHostList() de la fonction de mises à jour de la LiveData.

C'est la façon dont les fonctions d'oeil:

suspend fun pingHostAndUpdate(url: String, hostList: MutableList<Host>) {
    try {
        val before = Calendar.getInstance().timeInMillis
        val connection = URL(url).openConnection() as HttpURLConnection //Need error handling
        connection.connectTimeout = 5*1000
        connection.connect()
        val after = Calendar.getInstance().timeInMillis
        connection.disconnect()
        val diff = after - before
        updateHostList(url, diff, hostList)
    } catch (e: MalformedURLException) {
        Log.e("MalformedURLExceptionTAG", "MalformedURLException")
    } catch (e: IOException) {
        Log.e("IOExceptionTAG", "IOException")
    }
}

fun updateHostList(url: String, pingResult: Long, hostList: MutableList<Host>) {
    //All this on mainThread
    var foundHost: Host? = null
    var index = 0
    for (host in hostListLiveData.value!!) { 
        if (host.url == url) {
            foundHost = host
            break
        }
        index++
    } 
    if (foundHost != null) {
        viewModelScope.launch(Dispatchers.Main) {
            val host =  Host(foundHost.name, foundHost.url, foundHost.icon, pingResult)
            Log.d("TAAAG", "$host") 
            hostList[index] = host
            hostListLiveData.value = hostList
        }
    }
}

L'ensemble de ce qui se passe dans le viewModel. Actuellement, je suis à jour de ma liste en soumettant l'ensemble de la liste de nouveau lorsque je modifie une propriété d'un élément de la liste, ce qui semble horrible pour moi.

Ma question est: Comment puis-je mettre à jour une seule propriété de l'hôte et de l'avoir de rafraîchissement de l'INTERFACE utilisateur automatiquement?

Merci d'avance

Edit: Mon observateur ressemble à ceci:

viewModel.hostListLiveData.observe(this, Observer { adapter.updateData(it) })

Et updateData() ressemble à ceci:

fun updateData(freshHostList: List<Host>) {
    hostList.clear()
    hostList.addAll(freshHostList)
    notifyDataSetChanged()
}

@ArpitShukla, croyez-vous que j'aurais 2 fonctions de mise à jour? l'un pour l'affichage de la liste initiale et une autre pour la mise à jour sur l'élément de la liste? Ou aurais-je simplement mettre les deux notifyDataSetChanged() et notifyItemChanged() dans updateData()?

Edit2: changé mon appel de fonction pour la rendre asynchrone.

android-livedata kotlin
2021-11-23 22:53:04
1

La meilleure réponse

1

Vous pouvez envisager de mettre à jour des éléments observés à partir de hostListLiveData à l'aide de notifyItemChanged(position) au lieu de cela notifyDataSetChanged() dans votre adapter.

notifyItemChanged(position) est un élément de l'événement, dont la mise à jour seulement le contenu de l'élément.

EDIT:
Vous êtes à l'aide de notifyDataSetChanged() la mise à jour du contenu des données qui le fait de causer la nouvelle disposition et de relier les RecyclerView qui vous êtes attendait pas. Par conséquent, vous devez mettre à jour le contenu de vos données à l'aide de notifyItemChanged(position).

Je pense que vous pouvez créer une nouvelle fonction pour la mise à jour de votre RecyclerView dans la carte par exemple

fun updateHostAndPing(updatedHost: Host, position: Int) {
    hostList[position].apply {
        url = updatedHost.url
        averagePing = updatedHost.averagePing
    }
    notifyItemChanged(position)
}

et dans votre observateur, vous devrez peut-être vérifier si il est frais ou liste et liste mise à jour

viewModel.hostListLiveData.observe(this, Observer { 
    if (adapter.itemCount == ZERO) {
        adapter.updateData(it) 
    } else {
        it.forEachIndexed { index, host ->
            adapter.updateHostAndPing(host, index) 
        }
    }
})
2021-11-24 23:12:08

Cela fonctionne, mais je ne pense pas que cela est lié à LiveData. J'ai fait un simple recycleur de vue qui montre une liste pour tester cela, et je viens d'appeler l'adaptateur.notifyItemChanged(position). Ce n'travail, mais je ne vois pas comment il est lié à LiveData. Pourriez-vous préciser, s'il vous plaît? P. S.: je vais mettre à jour la question en montrant comment mon observateur fonctionne, je pense que cela va vous donner encore plus de contexte
SpawnTheTronix

Yep, il n'est pas sur la LiveData, il est dû à la manière dont la mise à jour de la RecyclerView. Vous utilisez notifyDataSetChanged() sur mettre à jour le contenu de vos données (par exemple, la mise à jour host et ping). L' notifyDataSetChanged() pleinement relier et de la nouvelle disposition toutes les données visibles.
Putra Nugraha

J'ai aussi essayé d'utiliser ListAdapter au lieu de RecyclerView.Adapteril atteint mon fonctionnalités souhaitées ainsi. Savez-vous ce qui est mieux, à l'aide de notifyDataSetChanged() ou un ListAdapter? Comme je le comprends notifyDataSetChanged()il met à jour la vue (ligne RecyclerView) qui vous le dire à la mise à jour.ListAdapter vérifie les différences dans la nouvelle liste et l'ancienne liste, puis met à jour le changé de domaine (c'est à dire un TextView) à la nouvelle valeur (bien que je ne suis pas sûr si c'mises à jour de la seulement le TextView ou la totalité de la ligne, auquel cas il n'y aurait pas de différence?).
SpawnTheTronix

ListAdapter sous le capot, à l'aide de AsyncListDiffer pour aider à calculer les différences entre les données enregistrées et ont fourni des données, et la façon dont il compare les données sont basées sur la condition définie dans le DiffUtil.ItemCallback. Autant que je sache, ListAdapter ne nouvelle disposition de votre RecyclerView, mais seulement de mettre à jour les données modifiées. Eh bien, ListAdapter est également une solution viable pour votre cas de toute façon
Putra Nugraha

Dans d'autres langues

Cette page est dans d'autres langues

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................