How do I refresh a RecyclerView + PagedListAdapter and scroll to top?
Asked Answered
O

4

8

I have a RecyclerView that is using a PagedListAdapter. I want to refresh the list (ie fetch the data from my DataSource) and scroll to the top automatically.

Here's my RecyclerView:

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    val adapter by lazy {
        DiscoveryListAdapter(activity)
    }
        recyclerView.let {
            it.adapter = adapter
            it.layoutManager = LinearLayoutManager(activity)
        }

Here's my Adapter:

class DiscoveryListAdapter() : PagedListAdapter<Discovery, DiscoveryListItemViewHolder>(DiscoveryDiffCallback()) {

    @SuppressLint("CheckResult")
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DiscoveryListItemViewHolder {
        //Extension function
        val view = parent.inflate(R.layout.discovery_detail_list_item)
        return DiscoveryListItemViewHolder(view)
    }

    override fun onBindViewHolder(viewHolder: DiscoveryListItemViewHolder, position: Int) {
        getItem(position)?.let {
            viewHolder.populateFrom(it)
        }
    }

    class DiscoveryDiffCallback : DiffUtil.ItemCallback<Discovery>() {
        override fun areItemsTheSame(one: Discovery, two: Discovery) =
                one == two

        override fun areContentsTheSame(one: Discovery, two: Discovery) =
                one == two
    }
}

I'm building the PagedList like so, subscribing to the LiveData object that is returned and calling submitList on the adapter:

    fun buildLivePagedList(): LiveData<PagedList<Discovery>> {
        val config = PagedList.Config.Builder()
                .setPageSize(PAGE_SIZE)
                .setEnablePlaceholders(false)
                .setPrefetchDistance(PAGE_SIZE * 2)
                .build()
        val factory = DiscoveryListDataSourceFactory()
        return LivePagedListBuilder(factory, config).build()
    }

I am using this line to refresh the list:

adapter.currentList?.dataSource?.invalidate()

This seems to refresh correctly, but it leaves the list scrolled halfway down. From there I don't know how to get the recyclerview to return to the top of the list. I have tried recyclerView.scrollToPosition(0) but no dice. No matter what, the list refreshes but does not scroll to the top. How do I do this?

Outspoken answered 2/5, 2019 at 23:54 Comment(2)
Turns out this is a bug in the paging library: issuetracker.google.com/issues/123834703Outspoken
Ironically I am having the opposite issue. I would like to refresh the data in the list while keeping the scroll position. Any idea of how you achieved this behavior?Flor
B
11

Try using the following code to Scroll to TOP:

pagedListAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
    override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
        if (positionStart == 0) {
            manager.scrollToPosition(0)
        }
    }
})
Bertiebertila answered 1/11, 2019 at 12:35 Comment(2)
This way is awesome! you saved me hours thanksUntitled
Best solution also to have a workaround to a known bug of paging3 that scroll at bottom of the list on first loadSeasonseasonable
R
2
class DiscoveryDiffCallback : DiffUtil.ItemCallback<Discovery>() {
    override fun areItemsTheSame(one: Discovery, two: Discovery) = false

    override fun areContentsTheSame(one: Discovery, two: Discovery) = false
}

Make your ItemCallback return false.Hope Helps!

Residency answered 19/3, 2020 at 11:44 Comment(1)
Yeah, it works, looks ugly but it works, otherwise scroll to top doesn'tJuni
D
0
    recyclerViewAdapter.currentList?.dataSource?.invalidate()

    recyclerView.postDelayed({
        recyclerView.layoutManager?.scrollToPosition(0)
    }, 400)
Dovap answered 27/12, 2019 at 15:46 Comment(2)
I tried adding a delay already, it doesn't work all of the time unfortunately.Outspoken
Turns out this is a bug in the paging library: issuetracker.google.com/issues/123834703Outspoken
T
0

try adapter.refresh()

 val movieListAdapter : PagingDataAdapter
    swiperefresh.setOnRefreshListener {
                movieListAdapter.refresh()
                swiperefresh.isRefreshing = false
    }
Tachygraphy answered 12/11, 2020 at 21:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.