Preference使用教程

Posted by alonealice on 2016-08-16

前言

这几天在看书时,意外地看到了Preference框架。相对于IOS SDK,这个功能在创建设置界面时会更加容易一点。开发者只需要编辑一个简单的xml文件,
就能开发出一个简单的设置界面。我在网上找了一些资料,看到这些资料都只是简单的介绍了这个功能的使用,不够具体时间上也比较早。现在我就详细的介绍一下它的使用。

使用场景

在Android手机中都有设置功能,许多的android应用也有相应的设置界面允许用户修改应用特性和行为。在为这些应用提供设置 功能时,开发者应该使 Android的Preference API构建一个与其他Android应用中的用户体验一致的界面(包括系统设置)。

概览

设置是使用在XML文件中声明的 Preference 类的各种子类构建而成,而不是使用View对象构建用户界面。
Preference 对象是单个设置的构建基块。每个Preference都有一个相应的键值对,可供系统用来将设置保存在应用设置的默认SharedPreferences文件中。当用户更改设置时,系统会自动更新SharedPreferences文件中的相应值。开发者只应在需要读取值以根据用户设置确定应用的行为时,才与关联的 SharedPreferences文件直接交互。
保存在SharedPreferences中的值主要是以下数据类型:
1.布尔型
2.浮点型
3.整型
4.长整型
5.字符串
6.字符串 Set
同时,开发者还需要专门的Activity和Fragment子类来显示UI。在3.0之前的Android版本中,开发者必须使用继承PreferenceActivity的activity;在3.0及更高的版本中,开发者可以使用普通的activity,同时使用继承PreferenceFragment的Fragment。如果需要多组设置,则可以使用PreferenceActivity为大屏幕创建双窗格布局。

首选项

Preference 对象是单个设置的构建基块,而其他的应用设置都是Preference的子类表示。每个子类都可以指定标题和默认值等内容,同时也有自己特定的属性和用户界面。最常用的首选项如下:
CheckBoxPreference:CheckBoxPreference 可创建一个列表项用于显示复选框,显示一个包含已启用或已禁用设置复选框的项目。
ListPreference:ListPreference 可创建一个项目用于打开包含选择列表的对话框,打开一个包含单选按钮列表的对话框。保存的值可以是任一受支持的值类型。
EditTextPreference:打开一个包含 EditText 小工具的对话框。
DialogPreference:打开一个dialog对话框。
MultiSelectListPreference:打开一个包含多选按钮列表的对话框。
SwitchPreference:一个switch选择器。

使用 XML 定义首选项

开发者开始在运行时直接实例化新的Preference对象,但是一般情况下,还是应该在XML文件中定义设置列表。这样更容易阅读和查看。
XML文件必须保存在 res/xml/ 目录中。 XML文件中的根节点必须是元素,在元素中可以添加任意的preference元素,每一个子项都会显示在设置列表中。

1
2
3
4
5
6
7
8
9
10
<EditTextPreference
android:key="edit_preference"
android:title="输入框"
android:summary="输入框"/>

<CheckBoxPreference
android:key="checkbox_preference"
android:title="开关"
android:summary="开关按钮"
android:defaultValue="true" />

在此示例中,两个项有3个共同的属性:
1.android:key:对于要保留数据值的首选项,必须拥有此属性。它指定系统在将此设置的值保存在 SharedPreferences 中时所用的唯一键(字符串)。
2.android:title:此属性为设置提供用户可见的名称。
3.android:summary:此属性为设置提供用户可见的概述。

使用设置组

如果开发者的设置列表中需要的设置选项比较多时,开发者可以使用设置组来进行分组,方便用户浏览、理解和处理设置。设置组可以有效的将一个长列表转化成多个段列表,方法有以下两种:

  • 使用标题

以分隔线分隔两组设置并为其提供标题,将每组 Preference 对象放入 PreferenceCategory 内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="主要组件">

<EditTextPreference
android:key="edit_preference"
android:title="输入框"
android:summary="输入框"/>

<CheckBoxPreference
android:key="checkbox_preference"
android:title="开关"
android:summary="开关按钮"
android:defaultValue="true" />

</PreferenceCategory>
</PreferenceScreen>
  • 使用子屏幕

将设置或设置组放入到子屏幕,点击时会跳转到子屏幕显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:key="button_voicemail_setting_key"
android:title="子页面"
android:persistent="false">
<Preference
android:summary="子页面文本框"
android:title="子页面文本框"
android:dependency="list_preference"
android:key="text_preference">

</Preference>
</PreferenceScreen>
</PreferenceScreen>

使用 Intent 跳转

在特殊情况,应用需要在设置页面跳转到应用的其他页面。所以要在用户选择首选项时调用Intent,需要在相应的Preference元素中添加intent。添加的intent的可以分为隐式和显式。

1
2
3
4
5
6
7
8
<Preference android:title="intent" >
<intent android:action="android.intent.action.VIEW"
android:data="http://www.google.com" />
</Preference>
<Preference android:title="intent" >
<intent android:targetClass="com.orange.studydemo.SecondActivity"
android:targetPackage="com.orange.studydemo"/>
</Preference>

intent可以添加一下属性:
android:action:要分配的操作(按照 setAction() 方法)。
android:data:要分配的数据(按照 setData() 方法)。
android:mimeType:要分配的 MIME 类型(按照 setType() 方法)。
android:targetClass:组件名称的类部分(按照 setComponent() 方法)。
android:targetPackage:组件名称的软件包部分(按照 setComponent() 方法)。

创建首选项的 Activity 和 Fragment

在XML文件配置完成之后,要在Activity中显式设置,需要扩展PreferenceActivity类。该类会根据自动保留每个Preference相关的设置。

1
2
3
4
5
6
7
public class SettingsActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}

在Android3.0及更高的版本中,应该使用PreferenceFragment来代替PreferenceActivity的使用。在PreferenceFragment中使用与在PreferenceActivity中使用基本一样。

1
2
3
4
5
6
7
public static class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}

同时需要在Activity中添加Fragment,代码如下:

1
2
3
4
5
6
7
8
9
10
public class SettingsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
}

设置默认值

在很多情况下,我们需要在用户第一次打开应用时就有默认值。所以需要对相应的Preference设置默认值。
设置默认值需要在XML文件中对Preference设置 android:defaultValue 属性。不同的 Preference 的默认值类型页不一样,比如CheckBoxPreference 的默认值是布尔值,而 ListPreference 的默认值类型是string。

然后需要在应用的主activity(设置为 的activity)中的oncreate()方法中调用setDefaultValues()方法

1
PreferenceManager.setDefaultValues(this,R.xml.main_activity,false);

其中第三个参数表示是否多次设置默认值。如果第三个参数设置为false,则应用只有在第一次调用activity时会设置默认值,如果设置为true,则每次应用打开时都会将值设置为默认值。

使用首选项标头

在大显示屏尤其是pad情况下,我们应该使用标头功能,而不是使用嵌套功能。使用标头功能的好处是在大屏幕上,preferenceActivity会自动提供双窗口显示。如下图:


而在小屏幕中则与嵌套差不多。
使用标头构建设置的方法:

  • 创建多个PreferenceFragment实例,每个实例都需要一个单独的XML文件。

  • 创建XML标头文件,其中每个设置都要对应一个相应的fragment。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
android:title="标头1"
android:summary="标头1" />
<header
android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
android:title="标头2"
android:summary="标头2" >
<extra android:name="someKey" android:value="someHeaderValue" />
</header>
</preference-headers>

在元素中可以使用bundle传递其他参数,在片段中可以使用getArgument()来获取参数,从而根据不同的参数使用同一个fragment加载不同的xml文件。

  • 将activity继承PreferenceActivity,并且实现onBuildHeaders()方法来显示标头,同时需要重写isValidFragment方法,对可以使用的fragment返回true。

1
2
3
4
5
6
7
8
9
10
public class SettingsActivity extends PreferenceActivity {
@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
}
@Override
protected boolean isValidFragment(String fragmentName) {
return true;
}
}

由于header是在android3.0之后添加的新功能,所以如果要兼容3.0之前的版本,我们需要在使用前文介绍的嵌套功能进行兼容。
activity主要的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
addPreferencesFromResource(R.xml.preference_headers_legacy);
}
}

@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
}

读取和监听首选项的变化

  • 读取首选项
    默认情况下,应用的所有首选项均保存到一个文件中,该文件可以通过调用静态方法 PreferenceManager.getDefaultSharedPreferences(),从应用内的任何位置访问。
1
2
3
SharedPreferences sharedPref = PreferenceManager.
getDefaultSharedPreferences(this);
String value = sharedPref.getString(key, defaultValue);
  • 监听首选项变化
    要在任一首选项发生更改时收到回调,Activity需要实现 SharedPreference.
    OnSharedPreferenceChangeListener 接口,并通过调用 registerOnSharedPreferenceChangeListener() 为 SharedPreferences 对象注册侦听器。
    该接口只有 onSharedPreferenceChanged() 一种回调方法,在回调中可以获取变化的Preference和相应的值。
1
2
3
4
5
6
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,String key) {
if (key.equals("key")) {
Preference preference = findPreference(key);
String value=sharedPreferences.getString(key, "");
}
}

为了能够保证侦听器不被当做垃圾回收,我们最好在 onResume() 和 onPause() 回调期间分别注册和注销
SharedPreferences.
OnSharedPreferenceChangeListener。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}

@Override
protected void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}