Android 的设置项 II-Reading Settings&Custom Preference

来源:互联网 发布:趣味学英语的软件 编辑:程序博客网 时间:2024/05/30 22:46

读取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 staticfinal 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 staticfinal Parcelable.Creator<SavedState> CREATOR =
            new Parcelable.Creator<SavedState>(){

        public SavedState createFromParcel(Parcelin) {
            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

0 0
原创粉丝点击