19

Currently I am using Android Architecture Components for App development everything is working along with paging library Now I want to remove recyclerview Item using PagedListAdapter to populate this we required to add a data source and from data source list is updating using LiveData no I want to remove a item from list notifyItemRemoved() is working from PagedList I am getting this exception:

java.lang.UnsupportedOperationException


java.util.AbstractList.remove(AbstractList.java:638)
Faris Jameel
  • 616
  • 2
  • 6
  • 22
  • I'm facing the same issue, @Fairs were you able to get a solution for this, https://stackoverflow.com/questions/54856941/how-to-clear-remove-all-items-in-page-list-adapter/54859736#54859736 – Sam Feb 25 '19 at 22:55
  • Did you configure any solution? Thanks! – M.SH Sep 20 '21 at 19:19

4 Answers4

10

Since you cannot directly modify the data that you have in the PagedList as I believe its immutable, the key to implementing the removal of items in this situation is maintaining a backing dataset somewhere in your code that represents all the data that you have received so far. An ArrayList worked for me.

When you want to remove an item, remove it from your backing dataset and call invalidate on your data source. This will trigger the loadInitial() call, in which you can pass your backing dataset to the callback.onResult() function. Your PagedListAdapter will use the DiffUtil.ItemCallback you supplied to smartly determine that there has been an update to its data and will update itself accordingly.

This tutorial helped me understand the correct flow to use in order to properly delete an item while using the paging library: https://medium.com/@FizzyInTheHall/implementing-swipe-to-delete-with-android-architecture-components-a95165b6c9bd

The repo associated with the tutorial can be found here: https://gitlab.com/adrianhall/notes-app

Tyler Bennett
  • 313
  • 2
  • 7
  • 6
    This solution appears to reload the entire RecyclerView as opposed to @cleanOK's second proposed solution. – AdamHurwitz Sep 01 '18 at 08:16
  • 2
    So when the loadInitial() call is triggered, you need to pass the backing data set to the callback, and the DiffUtil logic that has been set up will calculate the differences between what was in the RecyclerView's adapter and the newly passed in data. I don't believe that it will trigger a complete reload, as the DiffUtil's purpose is to avoid that - PagedListAdapter will use it to help compute fine-grained updates on a background thread. I definitely would recommend using Room in unison with the paging library if you can- but for cases where you can't, I think this is a good option. – Tyler Bennett Sep 01 '18 at 17:31
  • 1
    Thanks @Tyler Bennett. The current issue I'm having is the Firestore query is connected to a PagedAdapter via a custom DataSource. However, when I update the Firestore collection it does not update the PagedList. Rather, I need to invalidate the whole DataSource to reflect changes in the PagedList. I will connect Room to the PagedList instead to see if that allows for realtime updates to the UI. – AdamHurwitz Sep 02 '18 at 20:46
  • The problem with that is that when data source is invalidated, then DiffUtil has to check all items. Its not optimal. Only 1 item is deleted and DiffUtil has a callback to deal with 1 item only. – Malachiasz Dec 14 '18 at 13:45
  • 1
    Both links are not found. – CoolMind May 10 '23 at 13:38
4

Temporary hide item in list by call notifyItemUpdated() base on flags set in Pojo object

if(data?.hasVisible == false) {
            itemBinding.root.visibility = View.GONE
            itemBinding.root.layoutParams = RecyclerView.LayoutParams(0, 0)
        }else{
            itemBinding.root.visibility = View.VISIBLE
            itemBinding.root.layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
                RecyclerView.LayoutParams.WRAP_CONTENT)
        }
DungNguyen
  • 49
  • 1
3

I had same issue. My PagedList displayed items that DataSource factory fetched from server. I came up with two solutions how to remove item from list.

  1. Reload whole list. Make API call to remove item from server and then call pagedList.dataSource.invalidate(). Downside of this solution is that whole list is cleared and then all items received from server. Not exactly what I was looking for.
  2. Store results in Room. Then PagedList will get items directly from Room and PagedListAdapter will manage removing/adding items itself.

In DAO object

("SELECT * FROM YourModel")
fun getAll(): DataSource.Factory<Int, YourModel>
@Delete
fun delete(item: YourModel)

To update database as user scrolls list I implemented BoundaryCallback. It is being called when there are no more items to show in DB, it can be called at the end of same page, so I ensured to not execute same request few times (In my case list's key is page number).

class YourModelBoundaryCallback(private val repository: Repository) : PagedList.BoundaryCallback<YourModel>() {
    private val requestArray = SparseArray<Disposable>()
    private var nextPage = 1
    private var lastPage = 1

    override fun onZeroItemsLoaded() {
        if (requestArray.get(1) == null) {
            requestArray.put(1, repository.getFirstPage()
                    .subscribe({
                        lastPage = it.total / PAGE_SIZE + if (it.total % PAGE_SIZE == 0) 0 else 1
                        nextPage++
                    }))

        }
    }

    override fun onItemAtEndLoaded(itemAtEnd: YourModel) {
        if (nextPage > lastPage) {
            return
        }
        if (requestArray.get(nextPage) == null) {
            requestArray.put(nextPage, repository.getNextPage(nextPage)
                    .subscribe({
                        lastPage = it.total / PAGE_SIZE + if (it.total % PAGE_SIZE == 0) 0 else 1
                        nextPage++
                    }))
        }
    }

    override fun onItemAtFrontLoaded(itemAtFront: YourModel) {
        // ignored, since we only ever append to what's in the DB
    }
}

PagedList instance became this

private val pagedListConfig = PagedList.Config.Builder()
        .setEnablePlaceholders(false)
        .setPrefetchDistance(3)
        .setPageSize(PAGE_SIZE)
        .build()

val pagedList = LivePagedListBuilder(yourModelDao.getAll(), pagedListConfig)
        .setBoundaryCallback(YourModelBoundaryCallback(repository))
        .build()

And finally to remove item from adapter I just call yourModelDao.remove(yourModel)

cleanOK
  • 179
  • 1
  • 6
  • Does using Room update only the item in the UI that is removed or the entire RecyclerView? Currently with my Firestore custom DataSource implementation it is updating the entire RecyclerView when I invalidate data instead of just the one item desired. If Room solves this problem I will certainly connect Room to Firestore. – AdamHurwitz Sep 01 '18 at 08:24
  • After re-reading the documentation under [**Consider How Content Updates Work**](https://developer.android.com/topic/libraries/architecture/paging/data#consider-content-updates) it appears the only way to have realtime updates is via implementing Room with the PagedList: _If you're loading data directly from a Room database updates get pushed to your app's UI automatically._ – AdamHurwitz Sep 01 '18 at 08:37
  • 1
    @AdamHurwitz Correct, as far as I know, only Room implementation updates single item (or item range) instead of all items at once – cleanOK Sep 05 '18 at 12:12
  • **How can I disable realtime UI updates from Room to a PagedListAdapter?** I’ve built a feed of content where the position of the item is determined based on a qualityScore generated from user interactions. The feed’s UI updates in realtime and swipe-to-refresh when the underlying data changes. I would like to configure the option to disable realtime UI updates so the feed will not be constantly changing. – AdamHurwitz Sep 05 '18 at 17:02
  • @AdamHurwitz The simplest idea is 1. Store qualityScore in another DB entity 2. Configure your DAO object to access that qualityScore entity depending on some boolean flag – cleanOK Sep 06 '18 at 09:12
  • Thanks, that sounds like it would work. I kept it organized with just 1 query, 1 table by querying everything I needed in a specific timeframe from Firestore (my "backend") and then passing the data to the UI sorted by **qualityScore** with Room. Very simple! – AdamHurwitz Sep 06 '18 at 16:33
0

Adapter.notifyItemRemoved(position); appDatabase.userDao().deleteUser(user); It's work for me!

  • 23
    what if we are not using Room. – Faris Jameel Mar 02 '18 at 14:11
  • 2
    Yeah, and what if you know the object but not the position – Jacek Kwiecień Mar 14 '18 at 15:33
  • @Beyond Chen, does using Room update just the item in the UI that is removed or the entire RecyclerView? Currently with my Firestore implementation it is updating the entire RecyclerView when I invalidate data instead of just the one cell desired. If Room solves this problem I will certainly connect Room to Firestore. – AdamHurwitz Sep 01 '18 at 08:18
  • 7
    You notify the adapter then delete the user? that does not make sense! – hiddeneyes02 Sep 14 '18 at 16:25