Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding View.onSaveInstanceState() and View.onRestoreInstanceState() using View.BaseSavedState?

Assume you want to derive your own View class from an existing View implementation, adding a bit of value, hence maintaining a few variables which represent your View's state in a meaningful way.

It would be nice if your View would save its state automatically just like others do (if an ID is assigned) so you would want to override onRestoreInstanceState() and onSaveInstanceState().

Of course, you need to call the respective methods of your base class, and you need to combine your state information with that of your base class.

Obviously, the only safe way to do so is to wrap your super class' Parcelable in an own Parcelable such that the keys won't get mixed up.

Now there's View.BaseSavedState and its interesting getSuperState() method but I somehow fail to understand how this really adds value to just storing the base class' Parcelable in a Bundle along with the derived View's state values and return that. On the other hand, maybe some other system component will expect all InstanceState information to be of type View.AbsSavedState (e.g. such that getSuperState() can be called)?

Any experiences you're willing to share?

like image 737
class stacker Avatar asked Feb 15 '13 09:02

class stacker


2 Answers

To complement James Chen's answer, here is a full example of how to use this method, based on blog article by Charles Harley.

Code from the link:

public class LockCombinationPicker extends LinearLayout {
    private NumberPicker numberPicker1;
    private NumberPicker numberPicker2;
    private NumberPicker numberPicker3;

    public LockCombinationPicker(Context context) {
        this(context, null);
    }

    public LockCombinationPicker(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LockCombinationPicker(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        loadViews();
    }

    private void loadViews() {
        LayoutInflater.from(getContext()).inflate(R.layout.lock_combination_picker, this, true);
        numberPicker1 = (NumberPicker) findViewById(R.id.number1);
        numberPicker1.setMinValue(0);
        numberPicker1.setMaxValue(10);
        numberPicker2 = (NumberPicker) findViewById(R.id.number2);
        numberPicker2.setMinValue(0);
        numberPicker2.setMaxValue(10);
        numberPicker3 = (NumberPicker) findViewById(R.id.number3);
        numberPicker3.setMinValue(0);
        numberPicker3.setMaxValue(10);
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        return new SavedState(superState, numberPicker1.getValue(), numberPicker2.getValue(), numberPicker3.getValue());
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState savedState = (SavedState) state;
        super.onRestoreInstanceState(savedState.getSuperState());
        numberPicker1.setValue(savedState.getNumber1());
        numberPicker2.setValue(savedState.getNumber2());
        numberPicker3.setValue(savedState.getNumber3());
    }

    @Override
    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        // As we save our own instance state, ensure our children don't save and restore their state as well.
        super.dispatchFreezeSelfOnly(container);
    }

    @Override
    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        /** See comment in {@link #dispatchSaveInstanceState(android.util.SparseArray)} */
        super.dispatchThawSelfOnly(container);
    }

    /**
     * Convenience class to save / restore the lock combination picker state. Looks clumsy but once created is easy to maintain and use.
     */
    protected static class SavedState extends BaseSavedState {
        private final int number1;
        private final int number2;
        private final int number3;

        private SavedState(Parcelable superState, int number1, int number2, int number3) {
            super(superState);
            this.number1 = number1;
            this.number2 = number2;
            this.number3 = number3;
        }

        private SavedState(Parcel in) {
            super(in);
            number1 = in.readInt();
            number2 = in.readInt();
            number3 = in.readInt();
        }

        public int getNumber1() {
            return number1;
        }

        public int getNumber2() {
            return number2;
        }

        public int getNumber3() {
            return number3;
        }

        @Override
        public void writeToParcel(Parcel destination, int flags) {
            super.writeToParcel(destination, flags);
            destination.writeInt(number1);
            destination.writeInt(number2);
            destination.writeInt(number3);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }
}
like image 142
kinORnirvana Avatar answered Nov 16 '22 03:11

kinORnirvana


I think the design needs us, and as the name implies, to implement a subclass of View.BaseSavedState to store values by overriding Parcelable's interface.

TextView.SavedState is a good example

public static class SavedState extends BaseSavedState {
    int selStart;
    int selEnd;
    CharSequence text;
    boolean frozenWithFocus;
    CharSequence error;

    SavedState(Parcelable superState) {
        super(superState);
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeInt(selStart);
        out.writeInt(selEnd);
        out.writeInt(frozenWithFocus ? 1 : 0);
        TextUtils.writeToParcel(text, out, flags);

        if (error == null) {
            out.writeInt(0);
        } else {
            out.writeInt(1);
            TextUtils.writeToParcel(error, out, flags);
        }
    }

    @Override
    public String toString() {
        String str = "TextView.SavedState{"
                + Integer.toHexString(System.identityHashCode(this))
                + " start=" + selStart + " end=" + selEnd;
        if (text != null) {
            str += " text=" + text;
        }
        return str + "}";
    }

    @SuppressWarnings("hiding")
    public static final Parcelable.Creator<SavedState> CREATOR
            = new Parcelable.Creator<SavedState>() {
        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

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

    private SavedState(Parcel in) {
        super(in);
        selStart = in.readInt();
        selEnd = in.readInt();
        frozenWithFocus = (in.readInt() != 0);
        text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);

        if (in.readInt() != 0) {
            error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
        }
    }
}
like image 15
James Chen Avatar answered Nov 16 '22 03:11

James Chen