Cómo hacer LiveData<MutableList<T>> update al cambiar una propiedad de T?

0

Pregunta

Estoy haciendo una aplicación que obtiene la (pseudo) los valores de latencia haciendo una petición a alguna de las direcciones url y la grabación de cuánto tiempo va a tomar.

En primer lugar, yo uso posterior para obtener una respuesta JSON desde un servidor web. Esta respuesta contiene: el nombre del host (por ejemplo, Ebay reino unido), la dirección url del host (por ejemplo, www.ebay.co.uk), y la url de una imagen. Yo mapa esta respuesta en mi clase de datos que se parece a la siguiente:

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

el url es un var de la propiedad como antes de hacer las llamadas para obtener los valores de latencia, necesito agregar https:// en el fin de realizar la solicitud.

Estoy haciendo todo esto así:

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

El primer bucle for prepara mis datos. La línea después de que el bucle for presenta los datos para el reciclador adaptador, con el fin de mostrar el nombre de host, la dirección url y el icono de inmediato (funciona esto decir que tengo un trabajo observador de la LiveData), mientras que la estoy esperando los valores de latencia.

El segundo bucle for se llama a la función para calcular la latencia de valores para cada host y el updateHostList() la función de las actualizaciones de la LiveData.

Esta es la forma en que las funciones mira:

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

Todo esto sucede en el viewModel. Actualmente estoy actualizando mi lista por presentar la lista completa de nuevo cuando yo cambio una propiedad de un elemento de la lista, que parece horrible para mí.

Mi pregunta es: ¿Cómo puedo actualizar a una propiedad de host y tener que actualizar la interfaz de usuario de forma automática?

Gracias de antemano

Edit: Mi observador se parece a esto:

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

Y updateData() se parece a esto:

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

@ArpitShukla, ¿sugieren yo tendría 2 funciones de actualización? para mostrar la lista inicial y otro para actualizar el elemento de la lista? O acabo de poner tanto notifyDataSetChanged() y notifyItemChanged() en updateData()?

Edit2: se ha cambiado mi llamada a la función para hacerlo de forma asincrónica.

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

Mejor respuesta

1

Usted puede considerar para la actualización de los elementos observados a partir de hostListLiveData el uso de notifyItemChanged(position) en su lugar notifyDataSetChanged() en su adapter.

notifyItemChanged(position) es un elemento evento de cambio, que sólo actualizar el contenido del elemento.

EDITAR:
Estás usando notifyDataSetChanged() en la actualización del contenido de los datos que causan a relayout y volver a enlazar el RecyclerView que no está esperando. Por lo tanto, usted debe actualizar el contenido de sus datos mediante notifyItemChanged(position).

Creo que se puede crear una nueva función para la actualización de su RecyclerView en el adaptador por ejemplo,

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

y en su calidad de observador, puede que tenga que comprobar si es fresco lista o lista actualizada y

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

Esto funciona, pero no creo que esto está relacionado con LiveData. He hecho una simple reciclador de vista que muestra una lista para probar esto, y me acaba de llamar adaptador.notifyItemChanged(posición). Este hizo el trabajo, pero yo no veo cómo se relaciona con LiveData. Podrías aclarar, por favor? P. S.: voy a actualizar a la pregunta que muestra cómo mi observador funciona, creo que le dará algo más de contexto
SpawnTheTronix

Sí, no se trata de la LiveData, es debido a la forma en la actualización de la RecyclerView. Usted está utilizando notifyDataSetChanged() en la actualización de los contenidos de su base de datos (por ejemplo, la actualización de host y ping). El notifyDataSetChanged() totalmente de volver a enlazar y relayout todos los datos visibles.
Putra Nugraha

También he intentado usar ListAdapter en lugar de RecyclerView.Adapteralcanzó mi funcionalidad deseada así. ¿Sabes lo que es mejor, usar notifyDataSetChanged() o un ListAdapter? Como tengo entendido notifyDataSetChanged(), se actualiza la vista (fila en RecyclerView) que usted le diga que lo de la actualización.ListAdapter busca las diferencias en la lista de nuevo y el viejo de la lista y, a continuación, las actualizaciones de las que se han cambiado de campo (es decir, un TextView) para el nuevo valor (aunque no estoy seguro de si se actualiza el solo el TextView o de toda la fila, en cuyo caso no habría diferencia?).
SpawnTheTronix

ListAdapter bajo el capó es el uso de AsyncListDiffer para ayudar a calcular las diferencias entre los datos almacenados y los datos proporcionados, y cómo se comparan los datos se basa en la condición definida en el DiffUtil.ItemCallback. AFAIK, ListAdapter no relayout su RecyclerViewpero sólo la actualización de los datos modificados. Bien, ListAdapter también es una solución viable para su caso de todos modos
Putra Nugraha

En otros idiomas

Esta página está en otros idiomas

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