Jak udělat LiveData<MutableList<T>> aktualizovat, když jsem se změnit vlastnost T?

0

Otázka

Dělám aplikaci, která dostane (pseudo) hodnoty latence tím, že žádost, aby některé adresy url a nahrávání, jak dlouho to bude trvat.

Za prvé, jsem se použít dodatečná získat JSON odpověď z webového serveru. Tato reakce obsahuje: název hostitele (např. Ebay UK), url hostitele (např. www.ebay.co.uk), a adresu url obrázku. Já mapě to reakce na moje data třídy, které vypadá jako následující:

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

url je var ubytování jako před provedením volání získat latence hodnoty, musím přidat https:// tak, aby žádost.

Dělám vše tak jako:

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

První smyčka se připravuje mé údaje. Řádku po pro smyčce odešle data k recyklaci adaptér, aby bylo možné zobrazit název hostitele, adresa url a ikona rovnou (to všechno funguje, tj. mám pracovní pozorovatele pro LiveData), zatímco čekám na latence hodnoty.

Druhý cyklus for volá funkci pro výpočet hodnoty latence pro každého hostitele a updateHostList() funkce aktualizace LiveData.

To je, jak funguje vypadat:

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

To vše se děje v viewModel. V současné době jsem aktualizovat můj seznam předložením celý seznam znovu, když jsem se změnit jednu vlastnost jeden prvek seznamu, který se zdá být hrozné pro mě.

Moje otázka je: Jak mohu aktualizovat pouze jednu vlastnost z hostitele a mít ji aktualizovat UI automaticky?

Díky předem

Edit: Můj pozorovatele vypadá takto:

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

A updateData() vypadá takto:

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

@ArpitShukla, navrhuješ bych 2 aktualizace funkce? jeden pro zobrazení původního seznamu a další aktualizace na položku v seznamu? Nebo bych prostě dal obě notifyDataSetChanged() a notifyItemChanged() v updateData()?

Edit2: změnil volání funkce, aby se to asynchronní.

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

Nejlepší odpověď

1

Můžete zvážit aktualizaci položky pozorované z hostListLiveData pomocí notifyItemChanged(position) místo notifyDataSetChanged() ve vašem adapter.

notifyItemChanged(position) je položka změnit událost, která aktualizuje pouze obsah položky.

EDIT:
Používáte notifyDataSetChanged() na aktualizaci obsahu údajů, které způsobují relayout a znovu zaveďte vazbu RecyclerView které jste nečekali. Proto byste měli aktualizovat obsah vašich dat pomocí notifyItemChanged(position).

Myslím, že můžete vytvořit novou funkci pro aktualizaci RecyclerView v adaptéru, např.

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

a ve své pozorovatele, budete muset zkontrolovat, zda je nový seznam nebo a aktualizovaný seznam

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

To funguje, ale nemyslím si, že to souvisí s LiveData. Udělal jsem jednoduchý recycler view, která zobrazuje seznam pro tento test, a já jsem jen volal adaptér.notifyItemChanged(pozice). To dělal práci, ale nechápu, jak to souvisí s LiveData. Mohl byste to objasnit, prosím? P. S.: budu aktualizovat otázka ukazuje, jak můj pozorovatel funguje, myslím, že to vám dá nějaké další souvislosti
SpawnTheTronix

Jo, to není o LiveData, to je vzhledem ke způsobu aktualizace RecyclerView. Používáte notifyDataSetChanged() na aktualizaci obsahu vašich dat (např. aktualizace host a ping). Na notifyDataSetChanged() bude zcela znovu zaveďte vazbu a relayout všechny viditelné údaje.
Putra Nugraha

Také jsem se pokusil pomocí ListAdapter místo RecyclerView.Adapterto dosáhl požadované funkce stejně. Víte, co je lepší, pomocí notifyDataSetChanged() nebo ListAdapter? Pokud tomu dobře rozumím notifyDataSetChanged(), aktualizuje výhled (řádek v RecyclerView), který si říct, že k aktualizaci.ListAdapter kontroly na rozdíly v nový seznam a seznam starých, a pak aktualizuje změněné pole (tj. TextView) na novou hodnotu (I když si nejsem jistý, jestli se to aktualizuje pouze TextView nebo celý řádek, a v tom případě by tam být žádný rozdíl?).
SpawnTheTronix

ListAdapter pod kapotou je pomocí AsyncListDiffer na pomoc vypočítat rozdíly mezi uložená data, a za předpokladu, data, a jak je porovnat data je na základě definovaných stavu v DiffUtil.ItemCallback. AFAIK, ListAdapter nebude vaše relayout RecyclerView, ale aktualizovat pouze změněné údaje. No, ListAdapter je také schůdné řešení pro váš případ stejně
Putra Nugraha

V jiných jazycích

Tato stránka je v jiných jazycích

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