How to remove an item from PagedListAdapter in Android Paging Component
Asked Answered
C

2

22

I used Paging with Retrofit to loading list of Notifications data from REST API.

When I press delete button of a notification item then I call delete API to remove it. What is the proper way to remove it from PagedListAdapter after delete API response success? PagedList or PagedListAdapter does not support remove the item by index or object.

I tried to call DataSource.validate() but it reloads from begin, not from the current page.

Corrinacorrine answered 16/8, 2018 at 10:42 Comment(5)
do you use the Network+Database or network only?Reinhardt
I'm using the network only.Corrinacorrine
Ok put minima code that shows what you have doe so that it makes easier to know where to start helpingReinhardt
put what you have done so far. In minimal code explaining your problemReinhardt
Hey @Corrinacorrine have you found any solution? I'm facing same problem of my pagedListAdapter..?Aishaaisle
N
8

According to the official doc:

If you have more granular update signals, such as a network API signaling an update to a single item in the list, it's recommended to load data from network into memory. Then present that data to the PagedList via a DataSource that wraps an in-memory snapshot. Each time the in-memory copy changes, invalidate the previous DataSource, and a new one wrapping the new state of the snapshot can be created.

PagedList is immutable so you can't do any modifications against it. What you need to do is:

  1. maintain a mutable list L storing your server responses in your custom DataSource.
  2. pass it to LoadInitialCallback.onResult() (the PagedList is backing by L now).
  3. do whatever you like to L.
  4. call DataSource.invalidate() to pair with a new data source, so DataSource.loadInitial() is called to reflect the changes in step 3.
Norm answered 20/8, 2018 at 10:7 Comment(4)
This does actually work, but it does reload all the recyclerview after invalidate()Bidarka
will you please provide more explanation with example.Tidy
It's not a good idea. After you let it display several pages,you remove some item from list, then call DataSource.invalidate.You will see the recyclerview display the first page and scroll to head position from old original position, which is you don't want.Dygal
I think the idea is to load the remote data to a list as DataSource. You invalidate the dataSource, which cause create the new DataSource from your cached list. The problem is that you cached two copies into your memory. Also when the data from remote is huge, you won't be able to load all the data. @DygalRedfaced
Y
3

Not the most elegant solution, but it is working. For example it is much better to use this approach when using Firestore pagination when deleting item instead of re fetching whole list....

Also it can be optimized, because this is just a draft. Main Idea is to use dummy ViewHolder with blank view.

class PostAdapter(
    options: FirestorePagingOptions<Post>,
    private val feedViewModel: FeedViewModel
) : FirestorePagingAdapter<Post, RecyclerView.ViewHolder>(options) {

    companion object {
        private const val DELETED_VIEW_TYPE = 1
        private const val NORMAL_VIEW_TYPE = 2
    }

    private val deletedItems = arrayListOf<String>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = parent.getLayoutInflater()
        return when (viewType) {
            NORMAL_VIEW_TYPE -> {
                PostViewHolder(ItemPostBinding.inflate(inflater, parent, false))
            }
            else -> BindingViewHolder<ItemDeletedPostBinding>(
                ItemDeletedPostBinding.inflate(inflater, parent, false)
            )
        }

    }

    override fun getItemViewType(position: Int): Int {
        val item = getItem(position)?.toObject(Post::class.java)
        if (deletedItems.firstOrNull { it == item?.postId }.isNotNull()) {
            return DELETED_VIEW_TYPE
        } else {
            return NORMAL_VIEW_TYPE
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, model: Post) {
        if (holder is PostViewHolder) {
            holder.bind(model, feedViewModel)
        }
    }

    fun deleteItem(post: Post) {
        val postId = post.postId

        if (postId != null) {
            val element = currentList?.find { it.toObject(Post::class.java)?.postId == postId }
            val index = currentList?.indexOf(element)

            index?.let {
                deletedItems.add(postId)
                notifyItemChanged(it)
            }
        }
    }
}

Yuen answered 20/4, 2020 at 15:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.