How to properly implement onRestoreInstanceState() for a DialogPreference subclass?
Asked Answered
S

1

9

I am implementing my own custom DialogPreference subclass that has a SeekBar used for persisting an integer. I'm a little confused about what needs to go into onSaveInstanceState() and onRestoreInstanceState(). Specifically, do you need to update the UI widget that the user interacts with (in my case, the SeekBar widget) in onRestoreInstanceState()?

The reason I am confused is that the API doc article here tells you to do this:

@Override
protected Parcelable onSaveInstanceState() {
    final Parcelable superState = super.onSaveInstanceState();
    if (isPersistent()) {
        return superState;
    }

    final SavedState myState = new SavedState(superState);
    myState.value = mNewValue; //<------------ saves mNewValue
    return myState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    if (state == null || !state.getClass().equals(SavedState.class)) {
        super.onRestoreInstanceState(state);
        return;
    }

    SavedState myState = (SavedState) state;
    super.onRestoreInstanceState(myState.getSuperState());
    mNumberPicker.setValue(myState.value); //<------------ updates the UI widget, not mNewValue!
}

But looking at the source for some official Android Preference classes (EditTextPreference and ListPreference), the UI widget is not updated in onRestoreInstanceState(). Only the underlying value of the Preference is (in the example above, that would be mNewValue).

Here is the relevant source for EditTextPreference:

@Override
protected Parcelable onSaveInstanceState() {
    final Parcelable superState = super.onSaveInstanceState();
    if (isPersistent()) {
        return superState;
    }

    final SavedState myState = new SavedState(superState);
    myState.value = getValue(); //<---- saves mValue
    return myState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    if (state == null || !state.getClass().equals(SavedState.class)) {
        super.onRestoreInstanceState(state);
        return;
    }

    SavedState myState = (SavedState) state;
    super.onRestoreInstanceState(myState.getSuperState());
    setValue(myState.value); //<---- updates mValue, NOT the UI widget!
}

So, what's the consensus? Where I am supposed to update the UI widget (if I am supposed to update it at all...)?

Sugihara answered 6/1, 2013 at 11:56 Comment(0)
S
4

Okay, after some experimentation, it looks like updating the UI widget inside onRestoreInstanceState() is not the way to go, because it always seems to be null at that point. I don't know why they suggest it. Perhaps you have to do it if subclassing Preference, but there are different rules to follow when subclassing DialogPreference...? That would at least explain why ListPreference and EditTextPreference don't do it, because they subclass DialogPreference.

In fact, from what I've found, the UI widget does not need to be updated at all! It should have its own save/restore methods that handle its state management for you. For example, here is an excerpt of a DialogPreference subclass I made with a SeekBar widget in it:

@Override
protected Parcelable onSaveInstanceState() {
    final Parcelable superState = super.onSaveInstanceState();

    final SavedState myState = new SavedState(superState);
    myState.maxValue = getMaxValue(); //<---- saves mMaxValue
    myState.value = getValue(); //<---- saves mValue
    return myState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    if (state == null || !state.getClass().equals(SavedState.class))
    {
        super.onRestoreInstanceState(state);
        return;
    }

    SavedState myState = (SavedState) state;
    setMaxValue(myState.maxValue); //<---- updates mMaxValue
    setValue(myState.value); //<---- updates mValue
    super.onRestoreInstanceState(myState.getSuperState());
}

As you can see, I never update a SeekBar widget anywhere. The SeekBar will save/restore its state all by itself!

You'll also notice there are some slight deviations from what is suggested in the Android developer docs. I don't check if the DialogPreference is persistent before saving state, because then the mValue and mMaxValue properties would not get saved if it is. I also call super.onRestoreInstanceState() right at the end, as I found that it never works when it is called earlier.

These are just my findings so far. I'm not sure what the right way is, but what I have above seems to work.

UPDATE: @whatyouhide wants to know what the setValue and setMaxValue methods look like in my DialogPreference subclass. Here they are:

public void setValue(int value)
{
    value = Math.max(Math.min(value, mMaxValue), mMinValue);

    if (value != mValue)
    {
        mValue = value;
        persistInt(value);
        notifyChanged();
    }
}

public void setMaxValue(int maxValue)
{
    mMaxValue = maxValue;
    setValue(Math.min(mValue, mMaxValue));
}
Sugihara answered 14/1, 2013 at 11:3 Comment(2)
Are setValue and setMaxValue methods you defined in your custom DialogPreference? If so could you post the code for those methods?Druggist
Mmmm, thank you. My problem is that my DialogPreference.getValue() retrieves the value from some Views inside the dialog, and those views are still null pointers when onSaveInstanceState and onRestoreInstanceState get called.Druggist

© 2022 - 2024 — McMap. All rights reserved.