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:
- 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.
- You have to save the state in onPause method:
mBundleRecyclerViewState = new Bundle();
mListState = mRecyclerView.getLayoutManager().onSaveInstanceState();
mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, mListState);
- 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
How to Play Ringtone/Alarm Sound in Android
How to Change the Color of Alertdialog Title and the Color of the Line Under It
Android Dynamically Change Style at Runtime
Make Certain Area of Bitmap Transparent on Touch
How to Calculate Distance Between Two Locations Using Their Longitude and Latitude Value
How to Keep the Screen on in My App
How to Hide Soft Keyboard When Activity Starts
How to Switch Activity Without Animation in Android
How to Implement a Long Click Listener on a Listview
Changing Viewpager to Enable Infinite Page Scrolling
Get Contact Info from Android Contact Picker
Could Not Find Com.Android.Tools.Build:Gradle:3.0.0-Alpha1 in Circle Ci
How to Check Current Running Applications in Android
Start_Sticky Does Not Work on Android Kitkat
Using the Android Recognizerintent with a Bluetooth Headset
Android: How to Get Accurate Altitude