Como fazer LiveData<MutableList<T>> atualizar quando eu alterar uma propriedade de T?

0

Pergunta

Eu estou fazendo uma aplicação que fica o (pseudo) valores de latência através de um pedido a alguns urls e gravação de quanto tempo isso vai levar.

Primeiro, eu uso o retrofit, para obter uma resposta JSON a partir de um servidor web. Esta resposta se contém: o nome do host (por exemplo, o Ebay UK), a url do host (por exemplo, www.ebay.co.uk), e a url de uma imagem. Eu mapa esta resposta para a minha classe de dados que se parece com o seguinte:

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

o url é um var de propriedade como antes de fazer as chamadas para obter os valores de latência, eu preciso adicionar https:// para fazer a solicitação.

Eu estou fazendo tudo isso assim:

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) }
        }
    }
}

O primeiro loop for prepara os meus dados. A linha após o loop for submete os dados para o reciclador adaptador, a fim de mostrar o nome do host, a url e o ícone de imediato (isso tudo funciona por exemplo, eu tenho um trabalho de observador para o LiveData), enquanto eu estou esperando o valores de latência.

O segundo loop chama a função para calcular os valores de latência para cada host e o updateHostList() função atualiza a LiveData.

Esta é a forma como as funções olhada:

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
        }
    }
}

Tudo isso acontece em viewModel. Atualmente estou atualizando minha lista de submeter toda a lista novamente, quando eu alterar uma propriedade de um elemento da lista, que parece horrível para mim.

A minha pergunta é: Como posso actualizar apenas uma propriedade do host e ter que atualizar a INTERFACE do usuário automaticamente?

Obrigado antecipadamente

Edit: o Meu observador parecido com este:

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

E updateData() se parece com isso:

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

@ArpitShukla, você sugere que eu gostaria de ter 2 funções de atualização? um para mostrar a lista inicial e outro para a atualização no item da lista? Ou será que eu apenas colocar notifyDataSetChanged() e notifyItemChanged() em updateData()?

Edit2: mudou a minha chamada de função para torná-lo assíncrona.

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

Melhor resposta

1

Você pode considerar para atualizar os itens observados a partir de hostListLiveData usando notifyItemChanged(position) em vez disso notifyDataSetChanged() em seu adapter.

notifyItemChanged(position) é um item de evento de alteração, que apenas atualizar o conteúdo do item.

EDITAR:
Você está usando notifyDataSetChanged() na atualização do conteúdo dos dados que fazer um novo layout e religar a RecyclerView que você não está esperando. Portanto, você deve atualizar o conteúdo dos seus dados, utilizando notifyItemChanged(position).

Eu acho que você pode criar uma nova função, atualizar a sua RecyclerView o adaptador e.g.

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

e em seu observador, você pode precisar verificar se é fresco ou lista e lista atualizada

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

Isso funciona, mas eu não acho que isso está relacionado a LiveData. Eu fiz uma simples recycler modo de exibição que mostra uma lista para testar isso, e que eu chamei de adaptador.notifyItemChanged(posição). Este fez um trabalho, mas eu não vejo como ele está relacionado com LiveData. Você poderia esclarecer, por favor? P. S.: eu vou atualizar a questão mostrando como o meu observador funciona, eu acho que vai dar-lhe mais alguns contexto
SpawnTheTronix

Sim, ele não está sobre o LiveData, é devido à forma de atualizar o RecyclerView. Você está usando notifyDataSetChanged() na atualização do conteúdo dos dados (e.g. a atualização host e ping). O notifyDataSetChanged() vai totalmente de religação e um novo layout de todos os dados visíveis.
Putra Nugraha

Eu também tentei usar ListAdapter em vez de RecyclerView.Adapter, ele conseguiu o meu funcionalidade desejada também. Você sabe o que é melhor, usando notifyDataSetChanged() ou um ListAdapter? Tanto quanto eu entendo notifyDataSetChanged(), ele atualiza a vista (em linha RecyclerView) o que você diga a ele para atualização.ListAdapter verifica a existência de diferenças na nova lista e a lista de idade, e, em seguida, atualiza o campo alterado (por exemplo, um TextViewpara o novo valor (embora eu não tenho certeza se ele atualiza somente os TextView ou a linha inteira, caso em que não haveria diferença?).
SpawnTheTronix

ListAdapter sob o capô está usando AsyncListDiffer para ajudar a calcular as diferenças entre os dados armazenados e os dados fornecidos, e como ele se compara os dados com base na condição definida na DiffUtil.ItemCallback. AFAIK, ListAdapter não será um novo layout de seu RecyclerView, mas apenas atualizar os dados modificados. Bem, ListAdapter também é uma solução viável para o seu caso mesmo assim
Putra Nugraha

Em outros idiomas

Esta página está em outros idiomas

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