【问题标题】:Prevent PreferenceFragment from writing default preferences to SharedPreferences防止 PreferenceFragment 将默认首选项写入 SharedPreferences
【发布时间】:2016-10-01 23:48:47
【问题描述】:

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

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

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

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

【问题讨论】:

  • 如果用户没有更改任何特别偏好的内容,它将不会出现在存储所有值的偏好 xml 文件中
  • @pskink 我测试了这几次,当用户第一次打开首选项窗口时,PreferenceFragment 和 PreferenceActivity 提交所有首选项默认为 SharedPreferences。当我注意到它时,我很惊讶
  • ... and PreferenceActivity commit all preferences defaults to SharedPreferences ... 你的意思是物理文件/data/data/your.package/ shared_prefs/your.package_preferences.xml 填充了默认值?对于测试转储该文件,然后添加一些新的首选项,再次转储文件,更改该首选项并再次转储该文件
  • @pskink 我已经在许多设备上测试过这个,不同的 Android 版本,例如现在我有 root 的 Cyanogen 设备,在回复你之前我什至再次测试了它。删除shared_prefs/my.package_preferences.xml,打开设置窗口,这只是系统PreferenceFragment,单次调用addPreferencesFromResource(R.xml.prefs);,在我关闭应用程序后,我可以看到我的R.xml.prefs中定义的所有首选项都提交给shared_prefs/my.package_preferences.xml
  • @pskink 我用TwoStatePreference 源的链接更新了我的问题,其中提交发生在初始化时间。

标签: android android-preferences


【解决方案1】:

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

真正写入SharedPreferences 的唯一地方是Preference 类中的一堆persist[Type] 方法。 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();
        }
    }
}

接下来,在大约五个其他方法中调用TwoStatePreferencesetChecked 方法,其中两个调用可以产生默认值提交给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 中持久化值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    • 2011-07-23
    • 2012-09-28
    • 1970-01-01
    • 2012-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多