Android custom composite view save and restore state not working
android, android-custom-view, custom-view, onrestoreinstancestate, state
Solution
Figured it out... My custom view had the same ID for its `FrameLayout` as was being used for the specific instance of the custom view. The state was being properly saved by the instance and then overwritten (cleared) by the `FrameLayout` which had no state to persist.
I also changed its base-class to a `RelativeView` which made more sense.
In custom view's XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:id="@+id/addressInput"
android:background="@drawable/rounded_edit">
And the instance usage:
<com.myStuff.AddressInput
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/addressInput"
android:layout_marginTop="10dp"
app:addressMode="Domestic"
app:showSelect="true"
app:showClear="true" />
Changed to: (@+id/addressInput -> @+id/shippingAddress)
<com.myStuff.AddressInput
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="@+id/shippingAddress"
android:layout_marginTop="10dp"
app:addressMode="Domestic"
app:showSelect="true"
app:showClear="true" />
Makes you wish there was some scoping for IDs to prevent this kind of thing. Why should you have to know about a custom view's internals to make sure you avoid ID conflict?
Problem
Solved... I have a composite view that contains some other controls. I am trying to override save `onSaveInstanceState` and `onRestoreInstanceState`, but am getting a strange result. The `Parcelable state` argument to `onRestoreInstanceState` is not of my custom sub-class of `BaseSavedState`, `SavedState` and always seem to be `BaseSavedState.EMPTY_STATE`. (Look for the "always fails" code comment below... It seems that the problem is likely with the save portion, as the `SavedState.writeToParcel` is not being called after `onSaveInstanceState enters.` Almost as if whoever is calling `onSaveInstanceState` is throwing the result away before persisting it to the `Parcel`. If it makes a difference, this view is hosted inside a fragment. Any ideas? Here is my class definition: ``` public class AddressInput extends FrameLayout ``` Here is my `onSaveInstanceState` and `onRestoreInstanceState` pair: ``` @Override protected Parcelable onSaveInstanceState() { // Return saved state Parcelable superState = super.onSaveInstanceState(); return new AddressInput.SavedState( superState, mCurrentLookUp ); } @Override protected void onRestoreInstanceState( Parcelable state ) { // **** (state == BaseSavedState.EMPTY_STATE) is also always true // Cast state to saved state if ( state instance of AddressInput.SavedState ) // **** <--- always fails { AddressInput.SavedState restoreState = (AddressInput.SavedState)state; // Call super with its portion super.onRestoreInstanceState( restoreState.getSuperState() ); // Get current lookup mCurrentLookUp = restoreState.getCurrentLookup(); } else // Just send to super super.onRestoreInstanceState( state ); } ``` Here's my custom `BaseSavedState` sub-class (inner class of `AddressInput`) : ``` public static class SavedState extends BaseSavedState { private String mCurrentLookup; public SavedState(Parcelable superState, String currentLookup) { super(superState); mCurrentLookup = currentLookup; } private SavedState(Parcel in) { super(in); this.mCurrentLookup = in.readString(); } public String getCurrentLookup() { return mCurrentLookup; } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeString( this.mCurrentLookup ); } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public AddressInput.SavedState createFromParcel(Parcel in) { return new AddressInput.SavedState(in); } public AddressInput.SavedState[] newArray(int size) { return new AddressInput.SavedState[size]; } }; } ```