How to update single item using Paging 3 library
Asked Answered
C

4

22

I am trying to find a way to update single item in recycler view using PagingAdapter from Paging 3 library. I have found only one way with PagingAdapter.refresh() method. But this method force to load all list from network. Does anybody know how to implement it without loading all pages from network?

Cheetah answered 28/7, 2020 at 10:43 Comment(0)
D
21

Currently, the only way to update the backing dataset is to invalidate and reload the list. This is generally an acceptably cheap option for layered sources that use a cached layer (either in db such as room or in memory), although there is ongoing work to support more granular updates (see https://issuetracker.google.com/160232968).

In terms of layered source for now, you'll need to move your network calls into a RemoteMediator which you can register in Pager's constructor, and cache your network fetches into either a DB like with Room (which can generate a PagingSource implementation for you), or write an in-memory one yourself.

The codelab and DAC docs are a great resource for this, and have code samples to guide you!

Danettedaney answered 28/7, 2020 at 17:20 Comment(3)
Would the custom in-memory store be consumed by both the RemoteMediator and custom PagingSource? I'm thinking the custom PagingSource would observe for changes in the in-memory store and then invalidate itself. Would this still function as expected when the update task for a single item occurs asynchronously and the user scrolls the RecyclerView to a point where the item is no longer on screen?Bosom
On invalidation, Paging reloads using a key centered about the user's current position via PagingSource.getRefreshKey. The new generation is animated in via DiffUtil so yes, this should work.Danettedaney
Your RemoteMediator should not consume the cache, just write to it (except for tracking the next remote key to load with)Danettedaney
F
2

Example :

fun markItemAsRead(position: Int) {
        snapshot()[position].read = true
        notifyItemChanged(position)
    }

source : https://jermainedilao.medium.com/android-paging-3-library-how-to-update-an-item-in-the-list-52f00d9c99b2

Falco answered 16/8, 2021 at 10:54 Comment(1)
Note that this functions is inside an adapter.Psychopath
L
2

To accomplish this (note: here i don't use database for cache, only remote), we can create a mutable Flow of data list in the ViewModel that contains the items we want to manipulate. When observing the Flow of Paging 3, we can combine it with our local Flow and find the item we want to change. We can then change it before submitting it to the view to observe.

Here is an example of a simplified ViewModel that demonstrates this solution:

class ExampleViewModel : ViewModel() {

    private val _localDataList = MutableStateFlow(listOf<MyData>())

    // Observe the Paging 3 Flow and combine it with the local Flow
    val combinedDataList = paging3Flow.cachedIn(viewModelScope).combine(_localDataList) { paging, local  ->
        // Find and update the desired item in the list
        paging.map {
            if (it.id == local.id) local
            else  it
        }
    }

    // Method to update the item in the local Flow
    fun updateItem(item: MyData) {
        val updatedItem = getUpdatedItemFromServerUseCase()
        val newList = _localDataList.value.filterNot { it.id == updatedItem.id } // remove old version if any.
        _localDataList.value = newList + updatedItem
    }
}

If you want to remove items from the list, you can create a new data class that contains the item with an updateType enum that contains update and remove values. Depending on the update type, you can map the PagingData accordingly.

Here is an example of how you can use the updateType enum:

enum class UpdateType {
    UPDATE, REMOVE
}

data class UpdateData(
    val updateType: UpdateType,
    val item: MyData
)

class ExampleViewModel : ViewModel() {

    private val _localDataList = MutableStateFlow(listOf<UpdateData>())

    // Observe the Paging 3 Flow and combine it with the local Flow
    val combinedDataList = paging3Flow.combine(_localDataList.) { paging, local  ->
        // Find and update or remove the desired item in the list
        paging.map {
            local.find { localItem -> localItem.item.id == it.id }?.let { localItem ->
                when (localItem.updateType) {
                    UpdateType.UPDATE -> localItem.item
                    UpdateType.REMOVE -> null
                }
            } ?: it
        }.filter { it!=null }
    }

    // Method to remove the item from the local Flow
    fun updateItem(item: MyData) {
        removeItemFromServerUseCase()
        val newList = _localDataList.value.filterNot { it.item.id == item.id } // remove old version if any.
        _localDataList.value = newList + UpdateData(UpdateType.REMOVE, updatedItem)
    }
}

Lepidolite answered 5/4, 2023 at 4:38 Comment(1)
See a good example at sourcediving.com/…. Note that update and remove operations are quite easy to implement, but add is more difficult (used insertFooterItem instead).Assoil
T
-1
(adapterComment.snapshot().items as MutableList<Model>)[position].likeStatus = 0
adapterComment.notifyItemChanged(position)
Tsuda answered 19/1, 2022 at 19:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.