读取Preferences
默认情况下我们的APP的所有设置项都会保存在一个文件里, 我们可以在APP的任何位置通过静态方法PreferenceManager.getDefaultSharedPreference()访问它. 该方法返回SharedPreference对象, 所有的Preference中的键值对, 都保存在这里. 例如, 我们可以像这样在我们的任何一个Activity中读取设置项:
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");
监听设置项改变:
有时候我们需要监听设置项的改变, 以对我们的APP做出变化. 这时候需要使用sharedPreference.OnSharedPreferenceChangeListener接口, 注册接口的方法是SharedPreference对象的registerOnSharedPreferenceChangeListener()方法. 该监听接口只有一个回调方法,就是onSharedPreferenceChanged().栗子:
public class SettingsActivity extends PreferenceActivity
implements OnSharedPreferenceChangeListener {
public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
...
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
if (key.equals(KEY_PREF_SYNC_CONN)) {
Preference connectionPref = findPreference(key);
// Set summary to be the user-descriptionfor the selected value
connectionPref.setSummary(sharedPreferences.getString(key, ""));
}
}
}
在这个栗子中, 通过findPreference()方法查找跟key对应的Preference, 这样我们就可以对其进行修改. 当设置项是一个ListPreference或者其它多选项的设置项的时候, 我们应该调用setSummary()方法以显示设置项最新的状态.
我们应该在Activity的生命周期中注册和注销监听器, onResume()和onPause()方法比较合适:
@Override
protected void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
我们应该尽量创建实例对象来保存listener, 这样可以避免被回收, 这样写是推荐的方法:
SharedPreferences.OnSharedPreferenceChangeListener listener =
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// listenerimplementation
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
而不是这样:
prefs.registerOnSharedPreferenceChangeListener(
// Bad! Thelistener is subject to garbage collection!
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// listenerimplementation
}
});
管理网络设置:
从Android4.0开始, Android系统的设置功能开始允许用户查看他们设备上的APP在前台和后台的时候使用了多少网络流量. 用户们开始可以设置APP禁止后台访问网络. 为了避免用户这样做, 我们应该让用户可以设置APP访问网络的行为. 比如我们可以让用户设置APP访问网络的频率, 或者只有当WIFI环境才下载数据, 或者漫游的时候不访问网络等. 这样用户可以通过我们自己的APP的设置项来设置这些行为, 那么他就不太可能去通过系统设置功能去限制我们的APP了.
如果我们加入了相应的控制网络数据的设置项, 那么我们应该在我们的PreferenceActivity中增加一个intent filter, 并且指定action为ACTION_MANAGE_NETWORK_USAGE. 栗子:
<activity android:name="SettingsActivity" ... >
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
这个intent filter告诉Android该Activity是用来控制我的APP的网络访问的设置项的. 这样当用户访问Android的设置功能的时候, 一个APP设置按钮就会出现, 通过这个按钮可以访问我们的PreferenceActivity.
自定义Preference:
Android framework提供了多种Preference的子类供我们使用, 可以构建出丰富的APP设置项. 然而有些贪心不足的产品经理/项目经理依然不满足, 这时候就需要自定义了, 最常见的比如数字picker,日期picker等. 这种情况下我们需要继承Preference类, 来打造自己的Preference. 当我们继承Preference的时候, 我们有几个必须的工作要做:
1. 当用户选择某一个设置项的时候, 指定要显示的用户接口
2. 在适当的时候保存设置项的值.
3. 当被显示的时候, 记得初始化(默认值或者当前值).
4. 当需要的时候, 必须提供默认值(切忌返回空或者乱七八糟不确定的东西).
5. 如果Preference需要提供自己的UI, 比如一个Dialog, 那么应该处理好它的生命周期和状态, 比如用户旋转了屏幕的时候.
定义用户接口:
如果我们直接继承Preference, 那么需要实现onClick()接口来定义用户的点击行为. 但是我们并不需要总是这么做, 多数情况下我们可以通过继承DialogPreference类来显示一个对话框, 这样可以简化我们的操作. 当我们继承DialogPreference的时候我们必须在其构造方法中调用setDialogLayoutResource()方法来指定layout. 栗子:
public class NumberPickerPreference extends DialogPreference {
public NumberPickerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setDialogLayoutResource(R.layout.numberpicker_dialog);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
setDialogIcon(null);
}
...
}
保存设置的值:
我们可以在任何时间通过Preference类的persist*()方法保存一个设置项, 比如persistInt()用来保存一个整型, 用persistBoolean()保存一个boolean. 每个Preference仅可以保存一个值, 需要的保存时候我们必须使用persist*()方法.
我们调用persist*()方法的时机取决于我们使用的Preference类, 如果我们使用的是DialogPreference类, 那么我们需要在dialog关闭的时候调用保存方法, 即用户点击OK键的时候. 当一个DialogPreference关闭的时候, Android会调用onDialogClosed()方法, 该方法包含一个Boolean参数, 该参数描述的是用户执行的结果是不是肯定的, 如果为true, 则表示用户选择了肯定的选项, 那么我们应该保存该数据, 栗子:
@Override
protected void onDialogClosed(boolean positiveResult) {
// When the userselects "OK", persist the new value
if (positiveResult) {
persistInt(mNewValue);
}
}
在上面的代码中, mNewValue是保存设置项当前值的类变量. 调用persistInt()来将该值保存到SharedPreference文件中.
初始化设置项:
当Preference需要显示在屏幕的时候, Android会调用onSetInitialValue()方法来提醒APP是否需要对设置项初始化一个持久化值. 如果没有持久化值, 那么就需要设置一个默认值. onsetInitialValue()方法传入一个Boolean参数restorePersistedValue, 它用来指定一个设置项是否已经被持久化了, 如果为true, 则表示我们应该通过getPersised*()方法从持久化数据中取回设置的值. 我们可能会经常进行从持久化数据中取回设置项这样的操作. 如果restorePersistedValue的值是false, 则表示我们应该使用参数二传进来的默认值, 栗子:
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
if (restorePersistedValue) {
// Restore existing state
mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
} else {
// Set default state from the XML attribute
mCurrentValue = (Integer) defaultValue;
persistInt(mCurrentValue);
}
}
每个getPersisted*()方法都有一个默认值作为参数, 如果没能取到持久化数据, 就使用这个默认值. 但是我们不能使用defaultValue作为该方法的默认值, 因为如果restorePersistedValue为true的话, defaultValue是null.
提供一个默认值:
如果我们的Preference实例指定了一个默认值的话(通过android:defaultValue属性), 那么当Android实例化Preference的时候会调用它的onGetDefaultValue()方法来取得默认值. 我们必须实现该方法以便保存默认值到SharedPreference中, 栗子:
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInteger(index, DEFAULT_VALUE);
}
该方法的参数提供了所有我们需要的东西: 一个属性数组和android:defaultValue所在的序号. 我们必须这么做的原因是我们必须为属性指定一个本地的默认值, 以防其未定义.
保存和恢复设置项的状态:
跟View一样, 我们自定义的Preference同样需要保存自己的状态, 以防止Activity或者Fragment被重启(比如用户旋转了屏幕). 为了存取Preference的状态我们必须实现它的生命周期的回调方法onSaveInstanceState()和onRestoreInstanceState().
Preference的状态由一个实现了Parcelable接口的对象定义的. Androidframework提供了这样的一个对象让我们定义Preference的状态:Preference.BaseSaveState类. 我们应该继承该类, 实现一些它的方法, 并定义CREATOR对象. 对于大多数APP来说我们都可以copy下列的代码, 然后修改处理value的行:
private static class SavedState extends BaseSavedState {
// Member thatholds the setting's value
// Change thisdata type to match the type saved by your Preference
int value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
// Get the current preference's value
value = source.readInt(); // Change this to read the appropriate datatype
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Write the preference's value
dest.writeInt(value); // Change thisto write the appropriate data type
}
// Standardcreator object using an instance of this class
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];
}
};
}
上面这些代码加入到APP之后, 我们只需要实现onSaveInstanceState()和onRestoreInstanceState()方法就可以了:
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
// Check whetherthis Preference is persistent (continually saved)
if (isPersistent()) {
// No need to save instance state since it's persistent,
// use superclass state
return superState;
}
// Createinstance of custom BaseSavedState
final SavedState myState = new SavedState(superState);
// Set thestate's value with the class member that holds current
// setting value
myState.value = mNewValue;
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
// Check whetherwe saved the state in onSaveInstanceState
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save the state, so call superclass
super.onRestoreInstanceState(state);
return;
}
// Cast state tocustom BaseSavedState and pass to superclass
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
// Set thisPreference's widget to reflect the restored state
mNumberPicker.setValue(myState.value);
}
总结:
我们不需要自己使用listview等控件构造一个设置页面, 这样很麻烦, 不但要处理动作还要处理保存和读取, Android已经为我们提供了方便的工具—Preference类. 不但可以使用XML文件直接生成界面, 还可以自动保存设置的值, 读取也十分的方便, 真是居家旅行必备工具.
本节主要就是几个部分:
如何在XML中定义Preference
每个XML都使用<PreferenceScreen>作为根标签, 子项使用<Preference>的子类, 分组使用<PreferenceCategory>.子屏幕的使用<PreferenceScreen>. 还可以在<Preference>中加入<Intent>来指定需要启动的外部Activity.
以Android3.0位界限, 老版本使用PreferenceActivity, 新版本使用PreferenceFragment.
只需要在它们的onCreate()方法中使用addPreferenceFromResource()方法加载XML文件即可.
注意默认值的设置, 还可以使用headers为Preference锦上添花.
Headers本身与<PreferenceScreen>功能差不多, 但是在Android3.0以上推荐使用, 它更适用于处理大屏幕.
我们还可以自定义自己的Preference, 但是很麻烦, 要处理很多细节.
参考: http://developer.android.com/guide/topics/ui/settings.html