Saving scroll state of nested RecyclerViews - Part 2

In this post I’m going to address scroll state saving of nested RecyclerViews.

By default, if you scroll the parent RecyclerView, the nested RecyclerViews don’t save and restore state automatically.

You can observe this default behavior in the following video:

After scrolling the first nested list to position 5, if you scroll down the parent and that list is recycled, the scroll state is lost.

Saving scroll state

To fix this, at first, you might consider using LinearLayoutManager.findFirstVisibleItemPosition() to get the current position and RecyclerView.scrollToPosition() to restore it, but there are some issues with this approach:

  1. It doesn’t save the actual horizontal/vertical scroll performed by the user. If the user scrolls a view partially, the restored scroll position won’t match exactly what the user was seeing before.

  2. If you’re using a SnapHelper, the items won’t be positioned correctly.

An alternative to this approach, is using LayoutManager.onSaveInstanceState and LayoutManager.onRestoreInstanceState instead.

You can do this by mapping an unique key of the item to the LayoutManager state. Here’s an example:

After doing this, you should get this:

Fix SnapHelper position after detaching from window

Everything looks great until you start scrolling quickly. If you quickly scroll away and the nested list is detached from the window, the snap position will be lost.

In this example, the Views are snapped to the start, using GravitySnapHelper

As you can see, item 5 isn’t snapped to the left edge after attaching to the window again.

To fix this, you can manually snap the view to its final position on onViewDetachedFromWindow:

Here’s how it looks now:

Bonus - Activity/Fragment recreation

You can also save and restore the nested scroll state when your Activity/Fragment is recreated.

To do this, check ScrollStateHolder. This class isolates the logic from the previous examples and includes one optimization: it only saves the LayoutManager state if the RecyclerView was scrolled.

To use it, you need to pass it to your Adapter and use onSaveInstanceState and onRestoreInstanceState in your Activity/Fragment to save and restore the nested scroll states.

Check the sample code for ParentAdapter and MainActivity.

This sample is the same as the previous post and is published on Github: