title: Android: Saving ViewPager layout state without fragments tags: android,android-viewpager
If you want to save the state of your Views
in a ViewPager
you have to do the follow:
- Save the layout state of all your pager's views 1) on
onSaveInstanceState()
and 2) when an item is destroyed indestroyItem()
- Restore those in
onRestoreInstanceState()
and save them to a field - In
instatiateItem()
reference the above to restore the View's state
Here's an example implementation
package com.example.user.myapplication;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Saves the ViewPager's layout states.
*
* In the adapter instantiateItem() method, you must call restoreViewState().
* And in the adapter destroyItem() method, you must call saveViewState()
*/
public class SavingViewStateViewPager extends ViewPager {
public static final int TAG_RESTORE_VIEWPAGER_STATE_POS = "TAG_RESTORE_VIEWPAGER_STATE_POS".hashCode();
public static final String BUNDLE_VIEW_PAGER_POS = "pos";
public static final String BUNDLE_VIEW_STATE = "viewstate";
public static final String BUNDLE_PAGER_VIEW_STATE = "pagerviewstate";
public static final String BUNDLE_CHILDREN_VIEW_STATES = "childrenviewstates";
private HashMap<Integer, SparseArray<Parcelable>> mSavedViewStates = new HashMap();
public SavingViewStateViewPager(Context context) {
super(context);
}
public SavingViewStateViewPager(Context context, AttributeSet attrs) { super(context, attrs); }
private void setViewPositionTag(View v, int position) {
v.setTag(TAG_RESTORE_VIEWPAGER_STATE_POS, position);
}
private int getViewPositionFromTag(View v) {
Object tag = v.getTag(TAG_RESTORE_VIEWPAGER_STATE_POS);
return Integer.valueOf(tag.toString());
}
/**
* Called when you init your View for your ViewPager.
*
* Either restores state if we have any, or gives the view a positional number TAG so
* when we restore all the adapters view, we know which ones to put where.
* @param position
* @param view
*/
public void restoreViewState(int position, View view) {
SparseArray<Parcelable> state = mSavedViewStates.get(position);
if(state==null) {
Log.d(SavingViewStateViewPager.class.getSimpleName(), "State for view restore was null");
} else {
try {
view.restoreHierarchyState(state);
} catch(Exception e) {
Log.d(SavingViewStateViewPager.class.getSimpleName(), "Unable to restore view: " + e.getMessage());
}
}
setViewPositionTag(view, position);
}
/**
* Called when the ViewPager's adapter destroys the view -- we'll want to restore its state.
* @param position
* @param view
*/
public void saveViewState(int position, View view) {
SparseArray<Parcelable> hs = new SparseArray<>();
view.saveHierarchyState(hs);
mSavedViewStates.put(position, hs);
}
/**
* Iterates through all the views in the ViewPager,
* grabs their positional tag if exists
* and then stores their state in a bundle to be retrieved in onRestoreInstanceState().
* @return
*/
@Override
public Parcelable onSaveInstanceState() {
Bundle b = new Bundle();
b.putParcelable(BUNDLE_PAGER_VIEW_STATE, super.onSaveInstanceState());
final ArrayList<Parcelable> viewStates = new ArrayList<>();
for (int i = 0; i < getChildCount(); i++) {
final Bundle bundle = new Bundle();
View v = getChildAt(i);
Object tag = v.getTag(TAG_RESTORE_VIEWPAGER_STATE_POS);
if(tag!=null) {
try {
bundle.putInt(BUNDLE_VIEW_PAGER_POS, getViewPositionFromTag(v));
SparseArray<Parcelable> hierarchyState = new SparseArray<>();
v.saveHierarchyState(hierarchyState);
bundle.putSparseParcelableArray(BUNDLE_VIEW_STATE, hierarchyState);
viewStates.add(bundle);
} catch (Exception e) {
Log.d(SavingViewStateViewPager.class.getSimpleName(), "View probably didn't have a proper positional tag", e);
}
}
}
b.putParcelableArrayList(BUNDLE_CHILDREN_VIEW_STATES, viewStates);
return b;
}
/**
* From the Parcelable, grabs the view states, by position, and
* sets the mSavedViewStates -- so restoreViewState() has something to use.
* @param state
*/
@Override
public void onRestoreInstanceState(Parcelable state) {
if(!(state instanceof Bundle)) {
super.onRestoreInstanceState(state);
} else {
Bundle bundle = (Bundle) state;
super.onRestoreInstanceState(bundle.getParcelable(BUNDLE_PAGER_VIEW_STATE));
ArrayList<Parcelable> viewStates = bundle.getParcelableArrayList(BUNDLE_CHILDREN_VIEW_STATES);
if(viewStates==null) {
Log.d(SavingViewStateViewPager.class.getSimpleName(), "No view states to restore");
return;
}
for (int i = 0; i < viewStates.size(); i++) {
Bundle stateForChild = (Bundle) viewStates.get(i);
final SparseArray<Parcelable> childState = stateForChild.getSparseParcelableArray(BUNDLE_VIEW_STATE);
mSavedViewStates.put(stateForChild.getInt(BUNDLE_VIEW_PAGER_POS), childState);
}
}
}
}