RecyclerView store / restore state between activities
I found a better solution - this solution has the following benefits :
- RecyclerView state is saved and restored on rotation of phone
- 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);
}
}
}
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);
}
}
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);
}
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