PreferenceFragment
的当前行为:首次在屏幕上显示PreferenceFragment
时,会在SharedPreferences
XML资源中定义关联的PreferenceScreen
所有默认值。我测试了这几次,PreferenceFragment以及PreferenceActivity在用户打开“设置”活动时将所有首选项默认值写入SharedPreferences,即使他立即关闭它而不触及任何内容。
问题:在我的应用程序的下一个版本中,我决定更改一些默认用户首选项,它们将不适用于用户至少曾打开应用程序首选项的设备,因为PreferenceFragment
将所有默认值写入{ {1}}。我知道我可以通过覆盖SharedPreferences
中的所有值来重新应用新的默认值,不仅是默认值,还有用户选择的值。但是,在应用更新中重置用户首选项是完全不可接受的。所以问题在于,我们无法区分某个特定偏好是由用户明确设定还是由SharedPreferences
在屏幕上首次显示时所写的默认偏好。
我想要的:如果用户明确设置了一些偏好,无论他选择了什么,我都不应该用我更新的应用默认值来触摸它,即使用户选择与我的旧默认值一致。但是如果用户没有明确表示选择偏好,我希望我的新默认首选项能够通过应用更新开始为他工作。
那么:如何防止PreferenceFragment
向关联的SharedPreferences写入默认首选项值?
答案 0 :(得分:1)
在研究来源后,我找到了实现所要求行为的方法。
唯一真正写入SharedPreferences
的地方是persist[Type]
类中的Preference
方法。 Preference
的子类通常仅在单个内部方法中调用persist[Type]
方法,该方法在所有子类中具有相似的结构。例如,来自TwoStatePreference
的方法,即ChekBoxPreference
和SwitchPreference
的超类:
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中保留值。