RecyclerView store / restore state between activities

AndroidListviewAndroid Recyclerview

Android Problem Overview


I'm migrating my ListViews to RecyclerViews. With listviews I used the common technique described here to store and restore scroll position between activities.

How to do the same with RecyclerViews? the RecyclerView.onSaveInstanceState() seem to have protected access, so can't be used directly.

Android Solutions


Solution 1 - Android

Ok, so to answer my own question. As I understand it, since they've decoupled the layout code and the view recycling code (thus the name), the component responsible one for holding layout state (and restoring it) is now the LayoutManager used in your recyclerview.

Thus, to store state you use same pattern, but on the layout manager and not the recyclerview:

protected void onSaveInstanceState(Bundle state) {
     super.onSaveInstanceState(state);

     // Save list state
     mListState = mLayoutManager.onSaveInstanceState();
     state.putParcelable(LIST_STATE_KEY, mListState);
}

Restore state in the onRestoreInstanceState():

protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);

    // Retrieve list state and list/item positions
    if(state != null)
        mListState = state.getParcelable(LIST_STATE_KEY);
}

Then update the LayoutManager (I do in onResume()):

@Override
protected void onResume() {
    super.onResume();

    if (mListState != null) {
        mLayoutManager.onRestoreInstanceState(mListState);
    }
}

Solution 2 - Android

I found a better solution - this solution has the following benefits :

  1. RecyclerView state is saved and restored on rotation of phone
  2. RecyclerView state is saved and restored on returning to activity with RecyclerView (which wasn't destroyed whilst the other activity was showing - which means that onRestoreInstanceState() isn't called !!)

CODE

public class ActivityItemList extends AppCompatActivity
{
	private final String KEY_RECYCLER_STATE = "recycler_state";
	private RecyclerView mRecyclerView;
	private static Bundle mBundleRecyclerViewState;
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_list);//set to whatever layout name you have
		
		mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);//set to whatever view id you use
		// don't forget to set your adapter
	}
	
	@Override
	protected void onPause()
	{
		super.onPause();

		// save RecyclerView state
		mBundleRecyclerViewState = new Bundle();
		Parcelable listState = mRecyclerView.getLayoutManager().onSaveInstanceState();
		mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, listState);
	}
	
	@Override
	protected void onResume()
	{
		super.onResume();

		// restore RecyclerView state
		if (mBundleRecyclerViewState != null) {
			Parcelable listState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE);
			mRecyclerView.getLayoutManager().onRestoreInstanceState(listState);
		}
	}
}

Solution 3 - Android

Use this code in onPause() and onResume() to save and restore scroll position-

private Parcelable recyclerViewState;
recyclerViewState = mrecyclerView.getLayoutManager().onSaveInstanceState();//save
mrecyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);//restore

Solution 4 - Android

That's my solution, it restores both items and RecyclerView position

  1. save recycler view state in onSaveInstanceState method

    @Override protected void onSaveInstanceState(Bundle outState) { Parcelable listState = myRecyclerView.getLayoutManager().onSaveInstanceState(); // putting recyclerview position outState.putParcelable(SAVED_RECYCLER_VIEW_STATUS_ID, listState); // putting recyclerview items outState.putParcelableArrayList(SAVED_RECYCLER_VIEW_DATASET_ID,mDataset); super.onSaveInstanceState(outState); }

  2. Check savedInstanceState Bundle in onCreate method

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState==null){ getRemoteData(); // No saved data, get data from remote }else{ restorePreviousState(); // Restore data found in the Bundle } }

  3. Restore recycler view data if the screen has been rotated

    public void restorePreviousState(){ // getting recyclerview position mListState = mSavedInstanceState.getParcelable(SAVED_RECYCLER_VIEW_STATUS_ID); // getting recyclerview items mDataset = mSavedInstanceState.getParcelableArrayList(SAVED_RECYCLER_VIEW_DATASET_ID); // Restoring adapter items mAdapter.setItems(mDataset); // Restoring recycler view position mRvMedia.getLayoutManager().onRestoreInstanceState(mListState); }

Solution 5 - Android

I know i am late but still if it helps!!

Storing the recycler view position is lot simpler than what other answers have made it look like

Here's how you can do it

First create a member variable

Parcelable state;

Now,

@Override
protected void onPause() {
    super.onPause();
    state = recyclerView.getLayoutManager().onSaveInstanceState();
}

@Override
protected void onResume() {
    super.onResume();
    recyclerView.getLayoutManager().onRestoreInstanceState(state);
}

overide on pause and on resume methods with the above code and you are good to go!!

Solution 6 - Android

You can either use adapter.stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY which is introduced in recyclerview:1.2.0-alpha02

https://medium.com/androiddevelopers/restore-recyclerview-scroll-position-a8fbdc9a9334

but it has some issues such as not working with inner RecyclerView, and some other issues you can check out in medium post's comment section.

Or you can use ViewModel with SavedStateHandle which works for inner RecyclerViews, screen rotation and process death.

Create a ViewModel with saveStateHandle

val scrollState=
    savedStateHandle.getLiveData<Parcelable?>(KEY_LAYOUT_MANAGER_STATE)

use Parcelable scrollState to save and restore state as answered in other posts or by adding a scroll listener to RecyclerView and

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
           // save here
        }
    }

Solution 7 - Android

When you use recyclerView.getLayoutManager().onSaveInstanceState() don't forget to check for null:

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);

    if (recyclerView != null) {
        outState.putParcelable(SCROLL_POSITION, recyclerView.getLayoutManager().onSaveInstanceState());
    }
}

Solution 8 - Android

Considering that you defined a RecyclerView (mRecyclerView) and a LayoutManager (mLayoutManager) in your code and all is working fine so far, the solution on saving the position (mPosition) of your RecyclerView looks like this:

  1. Variables and constants used:

     private final String RECYCLER_POSITION_KEY = "recycler_position";
     private int mPosition = RecyclerView.NO_POSITION;
     private RecyclerView mRecyclerView;
     private LinearLayoutManager mLayoutManager;
     private static Bundle mBundleState;
    
  2. In onPause method:

     @Override
     protected void onPause()
     {
         super.onPause();
     
         // Save RecyclerView state
         mBundleState = new Bundle();
         mPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
         mBundleState.putInt(RECYCLER_POSITION_KEY, mPosition);
     }
    
  3. In onResume method:

     @Override
     protected void onResume()
     {
         super.onResume();
     
         // Restore RecyclerView state
         if (mBundleState != null) {
             mPosition = mBundleState.getInt(RECYCLER_POSITION_KEY);
             if (mPosition == RecyclerView.NO_POSITION) mPosition = 0;
             // Scroll the RecyclerView to mPosition
             mRecyclerView.smoothScrollToPosition(mPosition);
         }
     }
    
  4. In onSaveInstanceState method:

     @Override
     public void onSaveInstanceState(Bundle outState) {
         // Save RecyclerView state
         outState.putInt(RECYCLER_POSITION_KEY,  mLayoutManager.findFirstCompletelyVisibleItemPosition());
     
         super.onSaveInstanceState(outState);
     }
    
  5. In onRestoreInstanceState method:

     @Override
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
         // Restore RecyclerView state
         if (savedInstanceState.containsKey(RECYCLER_POSITION_KEY)) {
             mPosition = savedInstanceState.getInt(RECYCLER_POSITION_KEY);
             if (mPosition == RecyclerView.NO_POSITION) mPosition = 0;
             // Scroll the RecyclerView to mPosition
             mRecyclerView.smoothScrollToPosition(mPosition);
         }
     
         super.onRestoreInstanceState(savedInstanceState);
     }
    

Solution 9 - Android

public class MainActivity extends AppCompatActivity {
Parcelable recyclerViewState;
.......
@Override
    protected void onPause() {
        super.onPause();
        recyclerViewState = MainAnecdotesView.getLayoutManager().onSaveInstanceState();//save
    }
    @Override
    protected void onResume()
    {
        super.onResume();
        if(recyclerViewState!=null)
            MainAnecdotesView.getLayoutManager().onRestoreInstanceState(recyclerViewState);//restore
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkhusravView Question on Stackoverflow
Solution 1 - AndroidkhusravView Answer on Stackoverflow
Solution 2 - AndroidSomeone SomewhereView Answer on Stackoverflow
Solution 3 - Androidmahandis mehdiView Answer on Stackoverflow
Solution 4 - AndroidNicola GallazziView Answer on Stackoverflow
Solution 5 - AndroidShubham ShahView Answer on Stackoverflow
Solution 6 - AndroidThracianView Answer on Stackoverflow
Solution 7 - AndroidCoolMindView Answer on Stackoverflow
Solution 8 - AndroidCiprian BindiuView Answer on Stackoverflow
Solution 9 - AndroidVladView Answer on Stackoverflow