Best way to update a single element using Paging Library
Asked Answered
D

1

12

Which is the best way to update a single element when using the new paging library?

Let's say we have the Paging with network google sample using the PageKeyedSubredditDataSource. Imagine we want to make a change of a single element of RedditPost. So, we want to check if it is in the list and if so, update it. The update should not be as easy as calling invalidate() which will make a call to the first page (maybe the RedditPost is in the 5th page. We don't want to update all elements, just one).

Delft answered 31/5, 2018 at 0:58 Comment(8)
see developer.android.com/reference/android/arch/paging/DataSource, section Updating Paged Data, last paragraphGisborne
Thanks. But is there any way without using in-memory?Delft
are you using PagedListAdapter? if so, whats your DiffCallback like?Gisborne
Yes. val POST_COMPARATOR = object : DiffUtil.ItemCallback<UserEntity>() { override fun areContentsTheSame(oldItem: UserEntity, newItem: UserEntity): Boolean = oldItem == newItem override fun areItemsTheSame(oldItem: UserEntity, newItem: UserEntity): Boolean = oldItem.id == newItem.id }Delft
@DamiaFuentes did you succeed? I can successfully updated the List is jumping to beginning every time items are updated.Blip
I seriously don't get why room is using LiveData all over the place, but the paging library needs get fixed data or we need to implement some sort of in memory hacky way to invalidate all the datasources. Any solutions here? My database changes all the time and therefore emits liveData<TableEntry> values, which does not work well will paging library.Pietra
@KeivanEsbati Check the answer!Delft
@DamiaFuentes I will! ThanksBlip
D
2

Please note that all this works over the Paging with network google sample. Although that, the idea is there.

@Sarquella helped me with this solution. Add this classes to your project. Basically we are extending ViewHolder to be LifeCycle Owner, as it is already done by default with Activities and Fragments.

The LifecycleViewHolder:

abstract class LifecycleViewHolder(itemView: View) :
        RecyclerView.ViewHolder(itemView),
        LifecycleOwner {

    private val lifecycleRegistry = LifecycleRegistry(this)

    fun onAttached() {
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
    }

    fun onDetached() {
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
    }

    override fun getLifecycle(): Lifecycle = lifecycleRegistry
}

LifecycleOwner is a single method interface that denotes that the class has a Lifecycle. You can find more information here.

The LifecyclePagedListAdapter:

abstract class LifecyclePagedListAdapter<T, VH : LifecycleViewHolder>(diffCallback: DiffUtil.ItemCallback<T>) :
        PagedListAdapter<T, VH>(diffCallback) {

    override fun onViewAttachedToWindow(holder: VH) {
        super.onViewAttachedToWindow(holder)
        holder.onAttached()
    }

    override fun onViewDetachedFromWindow(holder: VH) {
        super.onViewDetachedFromWindow(holder)
        holder.onDetached()
    }
}

The LifecycleAdapter (in the case you need it):

abstract class LifecycleAdapter<VH : LifecycleViewHolder> :
        RecyclerView.Adapter<VH>() {

    override fun onViewAttachedToWindow(holder: VH) {
        super.onViewAttachedToWindow(holder)
        holder.onAttached()
    }

    override fun onViewDetachedFromWindow(holder: VH) {
        super.onViewDetachedFromWindow(holder)
        holder.onDetached()
    }
}

Then, extends MyAdapter to LifecyclePagedListAdapter<MyEntity, LifecycleViewHolder>(MY_COMPARATOR) and MyViewHolder to LifecycleViewHolder(view). You'll have to complete your classes based on what we have changed, accordingly. Now we can observe to a liveData object on MyViewHolder class. So we can add this to MyViewHolder class (assuming you're using Dependency Injection). Basically, we'll do the same we do for Fragments or Activities:

private lateinit var myViewModel: MyViewModel

init {
    (itemView.context as? AppCompatActivity)?.let{
        myViewModel = ViewModelProviders.of(it).get(MyViewModel::class.java)
    }
}

Then, inside the bind() method:

fun bind(myCell: MyEntity?) {
    myViewModel.myLiveData.observe(this, Observer {
        // Buala!! Check if it is the cell you want to change and update it.
        if (it != null && myCell != null && it.id == myCell.id) {
           updateCell(it)
        }
    })
}
Delft answered 27/9, 2018 at 6:11 Comment(5)
Hi Damia, might I ask is there any sample code that demonstrates all the necessary changes? I feel lost when I was going through the bind() method and updateCell.Unlucky
@Damia Fuentes Could you please provide any working sample for updating a specific page.Bield
@TonyThompson You can find a library implementing all those changes as well as a sample app here: github.com/Sarquella/LifecycleCellsFescennine
@BadhrinathCanessane You can find a library implementing all those changes as well as a sample app here: github.com/Sarquella/LifecycleCellsFescennine
For some reason, the observer is never called. I don't understand whyEstop

© 2022 - 2024 — McMap. All rights reserved.