Jak zaktualizować LiveData<MutableList<T><T>> po zmianie właściwości T?

0

Pytanie

Tworzę aplikację, która pobiera wartości (pseudo) opóźnienia, wysyłając zapytanie na niektóre adresy URL i nagrywa, ile czasu to zajmie.

Po pierwsze, używam retrofit aby uzyskać odpowiedzi JSON z serwera sieci web. Ta odpowiedź zawiera: nazwę hosta (np. Ebay UK), URL-adres hosta (np. www.ebay.co.uk) i adres URL obrazu. Ja сопоставляю odpowiedź z mojej klasy danych, który wygląda w następujący sposób:

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

adres url jest właściwością var, tak jak przed wykonaniem połączeń w celu uzyskania wartości opóźnienia muszę dodać https://, aby złożyć wniosek.

Ja robię to 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) }
        }
    }
}

Pierwsza pętla for przygotowuje moje dane. Wiersz po pętli for wysyła dane zasilacza recycler, aby natychmiast wyświetlić nazwę hosta, adres URL i ikona (to wszystko działa, Tzn. mam pracy obserwator dla LiveData), na razie czekam na wartości opóźnienia.

Druga pętla for powoduje funkcję do obliczania wartości opóźnienia dla każdego hosta, a funkcja updateHostList() aktualizuje dane LiveData.

Oto jak wyglądają funkcje:

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

Wszystko to dzieje się w modelu prezentacji. Obecnie jestem aktualizuję listę, wysyłając pełna lista ponownie, kiedy zdradzam jedną właściwość jednego elementu listy, co wydaje mi się okropnym.

Moje pytanie: Jak mogę zaktualizować tylko jedną właściwość hosta i zmusić go automatycznie aktualizować interfejs użytkownika?

Z góry dziękuję

Edit: Mój obserwator wygląda tak:

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

I updateData() wygląda tak:

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

@ArpitShukla, proponujesz, żeby u mnie było 2 opcje aktualizacji? jeden do wyświetlania początkowej listy, a drugi do aktualizacji elementu listy? Albo ja po prostu umieścił jak notifyDataSetChanged (), jak i notifyItemChanged() w updateData()?

Edit2: zmieniłem połączenie mojej funkcji, aby uczynić go asynchroniczna.

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

Najlepsza odpowiedź

1

Można rozważyć możliwość aktualizacji elementów, obserwowanych z hostListLiveData z pomocą notifyItemChanged(position) zamiast notifyDataSetChanged() W pracy adapter.

notifyItemChanged(position) to zdarzenie zmiany elementu, który aktualizuje tylko zawartość elementu.

ZMIANY:
Używasz notifyDataSetChanged() podczas aktualizacji treści danych, prowadzących do przekazywania i ponownego wiązania RecyclerView czego się nie spodziewasz. Dlatego należy zaktualizować treści swoich danych za pomocą notifyItemChanged(position).

Myślę, że można utworzyć nową funkcję do aktualizacji RecyclerView w karcie, np.

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

i w swoim the spectator, może trzeba sprawdzić, czy jest to świeże liście lub zaktualizowaną listą

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 działa, ale nie sądzę, że jest to związane z LiveData. Zrobiłem proste widok przetwornika, który pokazuje listę do sprawdzenia tego, i właśnie dzwonił adapter.notifyItemChanged(pozycja). To zadziałało, ale nie widzę, co to ma wspólnego z LiveData. Czy mógłby pan wyjaśnić, proszę? P. S.: Ja uaktualnię pytanie, pokazujący, jak działa mój obserwator, myślę, że to daje więcej kontekstu
SpawnTheTronix

Tak, nie chodzi o żywych danych, a w tym, jak jest aktualizowany RecyclerView. Używasz notifyDataSetChanged() o aktualizacji treści swoich danych (np. aktualizacja host i ping). W notifyDataSetChanged() będzie w pełni wykonana jest ponowne łączenie i przekazywanie wszystkich widocznych danych.
Putra Nugraha

Ja również próbowałem użyć ListAdapter zamiast RecyclerView.Adapteron również osiągnął wymaganej funkcjonalności. Czy wiesz, że lepiej używać notifyDataSetChanged() lub w ListAdapter? Jak rozumiem notifyDataSetChanged(), aktualizuje widok (ciąg RecyclerView) który każ mu się zaktualizować.ListAdapter sprawdzić obecność różnic w nowym liście i starym liście, a następnie aktualizuje zmieniona polu (tj. TextViewdo nowej wartości (chociaż nie jestem pewien, że ono aktualizuje tylko TextView lub cały wiersz, i w tym przypadku nie byłoby żadnej różnicy?).
SpawnTheTronix

ListAdapter pod maską znajduje zastosowanie AsyncListDiffer aby pomóc obliczyć różnice między zapisanymi danymi i uzyskanych danych, a także sposób ich porównania opiera się na określonym warunkiem, że w DiffUtil.ItemCallback. АФАИК, ListAdapter nie będzie przekazywać swoje RecyclerViewale aktualizuj tylko zmienione dane. Cóż, ListAdapter w każdym razie jest to również praktyczne rozwiązanie w twoim przypadku
Putra Nugraha

W innych językach

Ta strona jest w innych językach

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