Ako urobiť LiveData<MutableList<T>> aktualizovať keď som zmena majetku T?

0

Otázka

Robím aplikáciu, ktorá dostane (pseudo) latencie hodnoty tým, že požiadavku na niektoré adresy url a nahrávanie ako dlho to bude trvať.

Najprv som použiť retrofit dostať odpoveď JSON z webového servera. Táto odpoveď obsahuje: názov hostiteľa (napr. Ebay UK), url hostiteľa (napr. www.ebay.co.uk) a adresu url obrázku. Som mapu túto odpoveď na môj údaje trieda, ktorá vyzerá takto:

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

url je var majetku ako pred vykonaním hovory sa dostať latencia hodnoty, musím pridať https://, aby žiadosť.

Robím to všetko ako tak:

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

Prvý pre slučky pripravuje dáta. Riadok po pre slučky predloží údaje recyklátor adaptér, aby sa zobrazil názov hostiteľa, adresu url a ikonu rovno (to všetko pracuje, t. j. mám pracovných pozorovateľa pre LiveData), zatiaľ čo ja čakám na latenciu hodnoty.

Druhý pre slučky hovorov funkcia na výpočet latencie hodnoty pre každý hosť a updateHostList() funkcia aktualizuje LiveData.

To je, ako funkcie vzhľad:

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šetko sa deje v viewModel. V súčasnej dobe som sa aktualizuje môj zoznam odoslaním celý zoznam znovu, keď som sa zmeniť jednu vlastnosť jeden prvok zoznamu, ktorý sa zdá hrozné pre mňa.

Moja otázka je: Ako som sa aktualizovať len jednu vlastnosť hosť a majú ho obnoviť UI automaticky?

Vďaka vopred

Edit: Môj pozorovateľ vyzerá takto:

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

A updateData() vyzerá takto:

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

@ArpitShukla, navrhujete by som 2 update funguje? jeden zobrazuje počiatočné zoznam a ďalšie aktualizovať na položku zoznamu? Alebo by som len dal obe notifyDataSetChanged() a notifyItemChanged() v updateData()?

Edit2: zmenil moje volanie funkcie, aby to async.

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

Najlepšiu odpoveď

1

Môžete zvážiť, ak chcete aktualizovať položky pozorované z hostListLiveData pomocou notifyItemChanged(position) namiesto notifyDataSetChanged() vo vašom adapter.

notifyItemChanged(position) je položka, zmeniť udalosti, ktoré aktualizovať iba obsah položky.

ÚPRAVA:
Používate notifyDataSetChanged() na aktualizáciu obsahu údajov, ktoré spôsobuje na relayout a rebind na RecyclerView čo ste nečakal. Preto by ste si mali aktualizovať obsah svojich dát môžete použiť notifyItemChanged(position).

Myslím, že môžete vytvoriť novú funkciu pre aktualizáciu vášho RecyclerView v adaptér napr.

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

a vo vašom pozorovateľ, možno budete musieť skontrolovať, či je čerstvé a aktualizovaný zoznam alebo zoznam

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

Funguje to, ale nemyslím si, že to súvisí s LiveData. Urobil som jednoduchý recyklátor pohľad, ktorý zobrazuje zoznam tento test, a len som sa nazýva adaptér.notifyItemChanged(pozíciu). Toto robil prácu, ale nevidím, ako to súvisí s LiveData. Mohli by ste objasniť, prosím? P. S.: budem aktualizovať otázka ukazuje, ako môj pozorovateľ funguje, myslím, že vám niektoré ďalšie súvislosti
SpawnTheTronix

Áno, to nie je o LiveData, je vzhľadom na spôsob aktualizácii RecyclerView. Používate notifyDataSetChanged() na aktualizáciu obsahu vašich údajov (napr. aktualizácia host a ping). Na notifyDataSetChanged() bude plne rebind a relayout všetky viditeľné údajov.
Putra Nugraha

Tiež som sa snažil pomocou ListAdapter namiesto RecyclerView.Adapterto dosiahol svoj požadované funkcie rovnako. Viete, čo je lepšie, pomocou notifyDataSetChanged() alebo ListAdapter? Tak ďaleko, ako som pochopil notifyDataSetChanged()to aktualizuje zobrazenie (riadok v RecyclerView), ktoré vám povedať, že na aktualizáciu.ListAdapter kontroly na rozdiely v novom zozname a staré zoznam, a potom aktualizuje zmeniť pole (t. j. TextView) na novú hodnotu (aj keď nie som si istý, či sa aktualizuje len TextView alebo celý riadok, v ktorom prípade by tam byť žiadny rozdiel?).
SpawnTheTronix

ListAdapter pod kapotou je pomocou AsyncListDiffer pomôcť vypočítať rozdiely medzi uložené údaje a poskytla údaje, a ako sa to porovnať údajov je na základe definovanej podmienky v DiffUtil.ItemCallback. Pokiaľ viem, ListAdapter nebude vaše relayout RecyclerView,, ale iba na aktualizáciu upravené údaje. Dobre, ListAdapter je tiež vhodné riešenie pre vaše prípade aj tak
Putra Nugraha

V iných jazykoch

Táto stránka je v iných jazykoch

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