阻止PreferenceFragment将默认首选项写入SharedPreferences

时间:2016-06-02 09:55:51

标签: android android-preferences

PreferenceFragment的当前行为:首次在屏幕上显示PreferenceFragment时,会在SharedPreferences XML资源中定义关联的PreferenceScreen所有默认值。我测试了这几次,PreferenceFragment以及PreferenceActivity在用户打开“设置”活动时将所有首选项默认值写入SharedPreferences,即使他立即关闭它而不触及任何内容。

问题:在我的应用程序的下一个版本中,我决定更改一些默认用户首选项,它们将不适用于用户至少曾打开应用程序首选项的设备,因为PreferenceFragment将所有默认值写入{ {1}}。我知道我可以通过覆盖SharedPreferences中的所有值来重新应用新的默认值,不仅是默认值,还有用户选择的值。但是,在应用更新中重置用户首选项是完全不可接受的。所以问题在于,我们无法区分某个特定偏好是由用户明确设定还是由SharedPreferences在屏幕上首次显示时所写的默认偏好。

我想要的:如果用户明确设置了一些偏好,无论他选择了什么,我都不应该用我更新的应用默认值来触摸它,即使用户选择与我的旧默认值一致。但是如果用户没有明确表示选择偏好,我希望我的新默认首选项能够通过应用更新开始为他工作。

那么:如何防止PreferenceFragment向关联的SharedPreferences写入默认首选项值?

1 个答案:

答案 0 :(得分:1)

在研究来源后,我找到了实现所要求行为的方法。

唯一真正写入SharedPreferences的地方是persist[Type]类中的Preference方法。 Preference的子类通常仅在单个内部方法中调用persist[Type]方法,该方法在所有子类中具有相似的结构。例如,来自TwoStatePreference的方法,即ChekBoxPreferenceSwitchPreference的超类:

public void setChecked(boolean checked) {
    boolean changed = this.mChecked != checked;
    if(changed || !this.mCheckedSet) {
        this.mChecked = checked;
        this.mCheckedSet = true;
        this.persistBoolean(checked);
        if(changed) {
            this.notifyDependencyChange(this.shouldDisableDependents());
            this.notifyChanged();
        }
    }
}

接下来,在大约其他五种方法中调用setChecked TwoStatePreference方法,其中两个调用可以生成默认值以提交给SharedPreferences。这是第一个:

@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
    setChecked(restoreValue ? getPersistedBoolean(mChecked)
            : (Boolean) defaultValue);
}

第二名:

@Override
protected void onRestoreInstanceState(Parcelable state) {
    if (state == null || !state.getClass().equals(SavedState.class)) {
        // Didn't save state for us in onSaveInstanceState
        super.onRestoreInstanceState(state);
        return;
    }

    SavedState myState = (SavedState) state;
    super.onRestoreInstanceState(myState.getSuperState());
    setChecked(myState.checked);
}

以下是解决方案,自定义类,它将SwitchPreferenceCompat子类化并阻止上述两个调用中的提交:

public class MySwitchPref extends SwitchPreferenceCompat
{
    private boolean mAllowPersist;

    @Override
    protected boolean persistBoolean(boolean value) {
        if (mAllowPersist) {
            return super.persistBoolean(value);
        }
        return false;
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue,
            Object defaultValue) {
        mAllowPersist = false;
        super.onSetInitialValue(restoreValue, defaultValue);
        mAllowPersist = true;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        mAllowPersist = false;
        super.onRestoreInstanceState(state);
        mAllowPersist = true;
    }

    public MySwitchPref(Context context, AttributeSet attrs,
            int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public MySwitchPref(Context context, AttributeSet attrs,
            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public MySwitchPref(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MySwitchPref(Context context) {
        super(context);
    }
}

您应该将xml PreferenceScreen中的SwitchPreferenceCompat声明替换为此子类,并且所有这些都应该有效,我已对其进行了测试。如果你使用其他的SwitchPreference类型的首选项,你也应该类似地子类化并覆盖它们的行为。

注意:此解决方案依赖于当前com.android.support:preference-v7:23.4.0库的内部实现。它可能会随着将来的版本而改变,所以如果你使用其他库版本,或者使用非支持实现,你应该查看源代码,并确保没有其他调用在SharedPreferences中持久保存值,除了那两个,我覆盖。如果你使用Preference的其他子类,不仅是SwitchPreference,你应该检查其他调用以在SharedPreferences中保留值。