Recyclerview Store/Restore State Between Activities

RecyclerView store / restore state between activities

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);
}
}

How do I restore the previous RecyclerView state when a fragment is recreated?

Farid correctly says in the comment: These methods are used in cases when the Activity is destroyed, for example, when there is a lack of memory or when the configuration is changed (screen rotation and others). If you just clicked the Back button and thereby explicitly closed the Activity yourself, then these methods will not be executed.

If you want to have data after closing the application, then you must save this data to persistent memory.
The Preference Library is suitable for storing the simplest variables.
If the data structure is complex, then it is better to use Database.

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

How do you plan to save last saved position with RecyclerView.State?

You can always rely on ol' good save state. Extend RecyclerView and override onSaveInstanceState() and onRestoreInstanceState():

    @Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null && layoutManager instanceof LinearLayoutManager){
mScrollPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
}
SavedState newState = new SavedState(superState);
newState.mScrollPosition = mScrollPosition;
return newState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
if(state != null && state instanceof SavedState){
mScrollPosition = ((SavedState) state).mScrollPosition;
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null){
int count = layoutManager.getItemCount();
if(mScrollPosition != RecyclerView.NO_POSITION && mScrollPosition < count){
layoutManager.scrollToPosition(mScrollPosition);
}
}
}
}

static class SavedState extends android.view.View.BaseSavedState {
public int mScrollPosition;
SavedState(Parcel in) {
super(in);
mScrollPosition = in.readInt();
}
SavedState(Parcelable superState) {
super(superState);
}

@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mScrollPosition);
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}

@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}

How to save and restore scrolling position of the RecyclerView in a fragment when coming back from other activity to the same fragment?

After paying so many days, after trying many many solutions, finally found a working solution for my problem, thanks to https://stackoverflow.com/a/42563804/2128364

I've moved the working solution to another methods to make it work in the fragment.

Maybe this can save someone's day, Here is how it worked:

  1. onConfigurationChanged (as suggested in original solution), onSaveInstanceState or onViewRestoredState methods doesn't work, So you need onPause and onResume methods to save and restore the state respectively.
  2. You have to save the state in onPause method:
mBundleRecyclerViewState = new Bundle();    
mListState = mRecyclerView.getLayoutManager().onSaveInstanceState();
mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, mListState);

  1. And then Restore it in onResume Method:
if (mBundleRecyclerViewState != null) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mListState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE);
mRecyclerView.getLayoutManager().onRestoreInstanceState(mListState);
}
}, 50);
}
mRecyclerView.setLayoutManager(staggeredGridLayoutManager);

Restore recyclerview state

I found the problem. This code is being called on the fragment. But the problem was in the re-creation of the fragment in the activity. When I fixed recreating the fragment, list restores the position even without saving it state.

old code:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_supplication_detail);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
chapter = getIntent().getIntExtra(Constants.SELECTED_ITEM, 1);

fragmentSupplicationDetail = new FragmentSupplicationDetail();
Bundle extras = new Bundle();
extras.putInt(Constants.SELECTED_ITEM, chapter);
fragmentSupplicationDetail.setArguments(extras);
transaction.replace(R.id.container, fragmentSupplicationDetail, SUPPLICATION_FRAGMENT_TAG).commit();
}

fixes:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_supplication_detail);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
chapter = getIntent().getIntExtra(Constants.SELECTED_ITEM, 1);
if (getSupportFragmentManager().findFragmentByTag(SUPPLICATION_FRAGMENT_TAG) == null){
fragmentSupplicationDetail = new FragmentSupplicationDetail();
Bundle extras = new Bundle();
extras.putInt(Constants.SELECTED_ITEM, chapter);
fragmentSupplicationDetail.setArguments(extras);
Log.d(Constants.LOG_TAG, getClass().getSimpleName() + ": Создаем фрагмент ");
}else {
fragmentSupplicationDetail = (FragmentSupplicationDetail) getSupportFragmentManager().findFragmentByTag(SUPPLICATION_FRAGMENT_TAG);
Log.d(Constants.LOG_TAG, getClass().getSimpleName() + ": Восстанавливаем фрагмент ");
}
transaction.replace(R.id.container, fragmentSupplicationDetail, SUPPLICATION_FRAGMENT_TAG).commit();
}

Save RecyclerView state with a list

It looks like mLayoutManager has not been set. Since it is set in onLoadFinished(), either onLoadFinished() has not run or news.isEmpty() is true so mLayoutManager will still not be set.

It should be OK so just set mLayoutManager in onCreate(). That should solve the problem. You can also just check to see if mLayoutManager is null before using it in onSaveInstanceState().



Related Topics



Leave a reply



Submit