How to save RecyclerView's scroll position using RecyclerView.State?

陈文涛 picture 陈文涛 · Jan 7, 2015 · Viewed 116.1k times · Source

I have a question about Android's RecyclerView.State.

I am using a RecyclerView, how could I use and bind it with RecyclerView.State?

My purpose is to save the RecyclerView's scroll position.

Answer

Gökhan Barış Aker picture Gökhan Barış Aker · Mar 20, 2015

Update

Starting from recyclerview:1.2.0-alpha02 release StateRestorationPolicy has been introduced. It could be a better approach to the given problem.

This topic has been covered on android developers medium article.

Also, @rubén-viguera shared more details in the answer below. https://stackoverflow.com/a/61609823/892500

Old answer

If you are using LinearLayoutManager, it comes with pre-built save api linearLayoutManagerInstance.onSaveInstanceState() and restore api linearLayoutManagerInstance.onRestoreInstanceState(...)

With that, you can save the returned parcelable to your outState. e.g.,

outState.putParcelable("KeyForLayoutManagerState", linearLayoutManagerInstance.onSaveInstanceState());

, and restore restore position with the state you saved. e.g,

Parcelable state = savedInstanceState.getParcelable("KeyForLayoutManagerState");
linearLayoutManagerInstance.onRestoreInstanceState(state);

To wrap all up, your final code will look something like

private static final String BUNDLE_RECYCLER_LAYOUT = "classname.recycler.layout";

/**
 * This is a method for Fragment. 
 * You can do the same in onCreate or onRestoreInstanceState
 */
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    super.onViewStateRestored(savedInstanceState);

    if(savedInstanceState != null)
    {
        Parcelable savedRecyclerLayoutState = savedInstanceState.getParcelable(BUNDLE_RECYCLER_LAYOUT);
        recyclerView.getLayoutManager().onRestoreInstanceState(savedRecyclerLayoutState);
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelable(BUNDLE_RECYCLER_LAYOUT, recyclerView.getLayoutManager().onSaveInstanceState());
}

Edit: You can also use the same apis with the GridLayoutManager, as it is a subclass of LinearLayoutManager. Thanks @wegsehen for the suggestion.

Edit: Remember, if you are also loading data in a background thread, you will need to a call to onRestoreInstanceState within your onPostExecute/onLoadFinished method for the position to be restored upon orientation change, e.g.

@Override
protected void onPostExecute(ArrayList<Movie> movies) {
    mLoadingIndicator.setVisibility(View.INVISIBLE);
    if (movies != null) {
        showMoviePosterDataView();
        mDataAdapter.setMovies(movies);
      mRecyclerView.getLayoutManager().onRestoreInstanceState(mSavedRecyclerLayoutState);
        } else {
            showErrorMessage();
        }
    }