Modifying PagedList in Android Paging Architecture library

前端 未结 2 597
深忆病人
深忆病人 2021-02-01 22:34

I\'m currently looking into incorporating the Paging Architecture library (version 2.1.0-beta01 at the time of writing) into my app. One components is a list which

相关标签:
2条回答
  • 2021-02-01 23:25

    If you want to modify your list without going all the way down to the data layer, you will need to override submitList in your adapter, and then set a callback on your PagedList object. Whenever the PagedList changes, you can then copy those changes to your local dataset. This is not recommended but it's a pretty minimal hack to get working.

    Here's an example:

    class MyListAdapter : PagedListAdapter<MyDataItem, MyViewHolder>(MyDiffCallback()) {
    
        /**
         * This data set is a bit of a hack -- we are copying everything the PagedList loads into our
         * own list.  That way we can modify it.  The docs say you should go all the way down to the
         * data source, modify it there, and then bubble back up, but I don't think that will actually
         * work for us when the changes are coming from the UI itself.
         */
        private val dataSet = arrayListOf<MyDataItem>()
    
        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            //Forces the next page to load when we reach the bottom of the list
            getItem(position)
    
            dataSet.getOrNull(position)?.let {
                holder.populateFrom(it)
            }
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
            val view = parent.inflate(R.layout.my_view_holder)
            return MyViewHolder(view)
        }
    
        class MyDiffCallback : DiffUtil.ItemCallback<MyDataItem>() {
    
            override fun areItemsTheSame(oldItem: MyDataItem, newItem: MyDataItem) =
                    oldItem.id == newItem.id
    
            override fun areContentsTheSame(oldItem: MyDataItem, newItem: MyDataItem) =
                    oldItem == newItem
        }
    
        override fun submitList(pagedList: PagedList<MyDataItem>?) {
            pagedList?.addWeakCallback(listOf(), object : PagedList.Callback() {
                override fun onChanged(position: Int, count: Int) {
                    dataSet.clear()
                    dataSet.addAll(pagedList)
                }
    
                override fun onInserted(position: Int, count: Int) {
                    dataSet.clear()
                    dataSet.addAll(pagedList)
                }
    
                override fun onRemoved(position: Int, count: Int) {
                    dataSet.clear()
                    dataSet.addAll(pagedList)
                }
            })
            super.submitList(pagedList)
        }
    }
    
    0 讨论(0)
  • 2021-02-01 23:26

    You are correct in that a DataSource is meant to hold immutable data. I believe this is because Room and Paging Library is trying to have more opinionated design decisions and advocate for immutable data.

    This is why in the official docs, they have a section for updating or mutating your dataset should invalidate the datasource when such a change occurs.

    Updating Paged Data: 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.

    Source: https://developer.android.com/reference/android/arch/paging/DataSource


    With that in mind, I believe it's possible to solve the problem you described using a couple of steps.

    This may not be the cleanest way, as it involves 2 steps.

    You can get a reference the the snapshot that the PagedList is holding, which is a type MutableList. Then, you can just remove or update the item inside that snapshot, without invalidating the data source.

    Then step two would be to calling something like notifyItemRemoved(index) or notifyItemChanged(index).

    Since you can't force the DataSource to notify the observers of the change, you'll have to do that manually.

    pagedList.snapshot().remove(index) // Removes item from the pagedList
    adapter.notifyItemRemoved(index) // Triggers recyclerview to redraw/rebind to account for the deleted item.
    

    There maybe a better solution found in your DataSource.Factory. According to the official docs, your DataSource.Factory should be the one to emit a new PagedList once the data is updated.

    Updating Paged Data: To page in data from a source that does provide updates, you can create a DataSource.Factory, where each DataSource created is invalidated when an update to the data set occurs that makes the current snapshot invalid. For example, when paging a query from the Database, and the table being queried inserts or removes items. You can also use a DataSource.Factory to provide multiple versions of network-paged lists. If reloading all content (e.g. in response to an action like swipe-to-refresh) is required to get a new version of data, you can connect an explicit refresh signal to call invalidate() on the current DataSource.

    Source: https://developer.android.com/reference/android/arch/paging/DataSource

    I haven't found a good solution for this second approach however.

    0 讨论(0)
提交回复
热议问题