Cum sa faci LiveData<MutableList<T>> actualizare, când se modifica o proprietate a T?

0

Problema

Voi face o aplicație care devine (pseudo) valorile timpilor de latență de a face o cerere la unele url-uri și înregistrare cât timp va dura.

În primul rând, eu folosesc de reabilitare pentru a obține un răspuns JSON de la un server web. Acest răspuns conține: numele de gazdă (de exemplu, Ebay marea BRITANIE), url-ul de gazdă (de exemplu, www.ebay.co.uk), și o adresă url imagine. Eu hartă acest răspuns pe datele mele de clasa care arată în felul următor:

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

url-ul este un var de proprietate ca înainte de a face apeluri pentru a obține valorile timpilor de latență, am nevoie pentru a adăuga https://, în scopul de a face cererea.

Fac toate acestea astfel:

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

Prima bucla pregătește datele mele. Linie după buclă transmite date la reciclare adaptor, în scopul de a arăta numele de gazdă, url-ul și pictograma imediat (asta toate lucrările de exemplu am un lucru de observator pentru LiveData), în timp ce eu sunt de așteptare pentru valorile timpilor de latență.

Cea de-a doua buclă pentru apeluri de funcție pentru a calcula valorile timpilor de latență pentru fiecare gazdă și updateHostList() funcția actualizează LiveData.

Acesta este modul în care funcțiile uite:

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

Toate acestea se întâmplă în viewModel. În prezent îmi actualizez lista de depunerea întreaga listă din nou, atunci când am schimba o proprietate a unui element de listă, care pare oribil pentru mine.

Intrebarea mea este: Cum pot actualiza doar o proprietate de gazdă și să-l reîmprospăta UI automat?

Multumesc anticipat

Edit: Mi observator arată astfel:

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

Și updateData() arată astfel:

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

@ArpitShukla, nu sugerez că ar fi 2 funcțiile de actualizare? unul pentru a arăta lista inițială și alta să actualizare pe element din listă? Sau aș pune ambele notifyDataSetChanged() și notifyItemChanged() în updateData()?

Edit2: mi-a schimbat funcția de a face apel asincron.

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

Cel mai bun răspuns

1

Puteți lua în considerare pentru a actualiza elementele observate de hostListLiveData folosind notifyItemChanged(position) în loc notifyDataSetChanged() în adapter.

notifyItemChanged(position) este un element de schimbare de eveniment, care se actualizează numai conținutul elementului.

EDIT:
Utilizați notifyDataSetChanged() privind actualizarea conținutului de date care cauzează să relayout și legăm din nou RecyclerView care nu te aștepți. Prin urmare, ar trebui să actualizați conținutul de date folosind notifyItemChanged(position).

Cred că s-ar putea crea o nouă funcție pentru actualizarea RecyclerView în adaptorul de exemplu

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

și în observator, poate fi necesar să verificați dacă acesta este proaspăt listă sau și lista actualizată

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

Aceasta funcționează, dar nu cred că acest lucru este legat de LiveData. Am făcut o simplă reciclare vedere care arată o listă pentru a testa acest lucru, și am sunat adaptor.notifyItemChanged(poziție). Acest lucru a facut dar nu vad cum aceasta este legată de LiveData. Ai putea clarifica, te rog? P. S.: voi actualiza întrebare care arată modul meu de observator funcționează, cred că vă va oferi ceva mai mult context
SpawnTheTronix

Da, este vorba despre LiveData, este din cauza modului actualizarea RecyclerView. Utilizați notifyDataSetChanged() privind actualizarea conținutului de date (de exemplu, actualizarea host și ping). La notifyDataSetChanged() va complet rebind și relayout toate vizibile de date.
Putra Nugraha

Am încercat, de asemenea, folosind ListAdapter în loc de RecyclerView.Adaptera realizat meu dorită funcționalitate, precum și. Știi ce este mai bine, folosind notifyDataSetChanged() sau o ListAdapter? Din câte am înțeles notifyDataSetChanged(), se actualizează vedere (rând în RecyclerView), care spune că pentru a actualiza.ListAdapter controale pentru diferențele în listă nouă și vechea listă, și apoi actualizează schimbat domeniu (de exemplu, un TextView) la noua valoare (deși nu sunt sigur dacă se actualizează numai TextView sau întregul rând, în cazul în care nu ar fi nici o diferenta?).
SpawnTheTronix

ListAdapter sub capota este folosind AsyncListDiffer pentru a ajuta la calcularea diferențelor între datele stocate și datele furnizate, și cum se compară datele se bazează pe definit starea în DiffUtil.ItemCallback. AFAIK, ListAdapter nu va relayout ta RecyclerView, dar numai actualiza datele modificate. Ei bine, ListAdapter este, de asemenea, o soluție viabilă pentru cazul tau oricum
Putra Nugraha

În alte limbi

Această pagină este în alte limbi

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