gbaccetta
gbaccetta

Reputation: 4577

ListAdapter submitList() - How to scroll to beginning

I have an issue with my RecyclerView and ListAdapter.

Through an API, I receive items in a ascending order from older to newer.

My list is then being refreshed by calling the submitList(items) method.

However, since everything is asynchronous, after receiving the first item, the RecyclerView remains on the position of the first item received and showed. Since in the ListAdapter class there is no callback when the submitList() method completed, I cannot find a way to scroll after the updates to one of the new items that has been added.

Is there a way to intercept when ListAdapter has been updated ?

Upvotes: 56

Views: 14251

Answers (6)

Jiyeh
Jiyeh

Reputation: 5307

For my case, just override onCurrentListChanged in your adapter class and call notifyDataSetChanged()

override fun onCurrentListChanged(
    previousList: MutableList<InboxMessage>,
    currentList: MutableList<InboxMessage>
) {
    super.onCurrentListChanged(previousList, currentList)
    notifyDataSetChanged()
}

Upvotes: 0

Shivanand Darur
Shivanand Darur

Reputation: 3224

listAdapter.submitList(yourNewList) { yourRecyclerView.smoothScrollToPosition(0) }

Upvotes: 1

gallosalocin
gallosalocin

Reputation: 995

Kotlin : Very simple method to do this.

listAdapter.submitList(yourNewList) { 
    yourRecyclerView.scrollToPosition(0) 
}

Upvotes: 63

Martin Zeitler
Martin Zeitler

Reputation: 76799

The method to override is called:

public void onCurrentListChanged(List<T> previousList, List<T> currentList)

As the documentation reads:

Called when the current List is updated.


The inline documentation of the ListAdapter reads:

This class is a convenience wrapper around AsyncListDiffer that implements Adapter common default behavior for item access and counting.

And later on it reads:

Advanced users that wish for more control over adapter behavior, or to provide a specific base class should refer to AsyncListDiffer, which provides custom mapping from diff events to adapter positions.

This means, you'd need to extend the regular RecyclerView.Adapter, combined with a custom implementation of the AsyncListDiffer; and not the ListAdapter (which is just a convenience wrapper, with the stated basic functionality).

One can use the ListAdapter class as a template for that (not extending it is the clue), but the resulting class can then be extended and reused. else there is no chance to get control over the desired callback.

Upvotes: 6

Anton Holovin
Anton Holovin

Reputation: 5673

I have checked ListAdapter source code and I see that there is onCurrentListChanged method.

You can simply override it in your custom adapter. You can also pass a listener to the adapter to catch the event and scroll your RecyclerView to the beginning.

Your adapter:

typealias ListChangeListener = () -> Unit

class Adapter(private val listChangeListener: ListChangeListener) : ListAdapter<Item, Adapter.ViewHolder>(Callback) {

    override fun onCurrentListChanged(previousList: List<Item>, currentList: List<Item>) {
        super.onCurrentListChanged(previousList, currentList)

        // Call your listener here
        listChangeListener()
    }

    // Other methods and ViewHolder class
}

Create the adapter in your Activity or Fragment:

recyclerView.adapter = Adapter { recyclerView.scrollToPosition(0) }

Upvotes: 8

David Liu
David Liu

Reputation: 9600

Not specific to ListAdapter, but it should work nonetheless:

Just use adapter.registerAdapterDataObserver(RecyclerView.AdapterDataObserver) and override the relevant onItemXXX method to tell your RecyclerView to scroll to whatever position.

As an example:

    adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
            (recycler_view.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(positionStart, 0)
        }
    })

Upvotes: 44

Related Questions