一.基础知识
我们在使用Android手机时,可能会经常用到其中的设置界面,如下所示:
图1 Android系统中的设置界面
而且对于我们自己开发的一个完整的程序来说,我们也会需要给用户提供一个设置界面使用户可以对程序的一些参数进行设置。因此,我们就来学习下如何实现符合我们需要的一个设置界面,及其背后的数据持久化功能。
二.实例开发
2.1 SharedPreferences
在具体介绍Android的设置界面的实现之前,我们先来介绍一下预备知识,就是Android数据持久化方法中最简单的一种,即使用Preferences的键值对存储方式。这种方式主要用来存储比较简单的一些数据,而且是标准的Boolean、Int、Float、Long、String等类型。
android.content.SharedPreferences是一个接口,用来获取和修改持久化存储的数据。有三种获取系统中保存的持久化数据的方式:
1. public SharedPreferences getPreferences (int mode)
通过Activity对象获取,获取的是本Activity私有的Preference,保存在系统中的xml形式的文件的名称为这个Activity的名字,因此一个Activity只能有一个,属于这个Activity。
2. public SharedPreferences getSharedPreferences (String name, int mode)
因为Activity继承了ContextWrapper,因此也是通过Activity对象获取,但是属于整个应用程序,可以有多个,以第一参数的name为文件名保存在系统中。
3. public static SharedPreferences getDefaultSharedPreferences (Context context)
PreferenceManager的静态函数,保存PreferenceActivity中的设置,属于整个应用程序,但是只有一个,Android会根据包名和PreferenceActivity的布局文件来起一个名字保存。
通过以上方式取得SharedPreferences后就可以对数据进行读取或者保存了。
保存方式如下:
String STORE_NAME = "Settings";
SharedPreferences settings = getSharedPreferences(STORE_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putInt("sourceType", 0);
editor.commit();
获得SharedPreferences,如果需要进行保存等修改操作,首先得通过其edit()方法获得SharedPreferences.Editor,然后就可以通过putInt、putString等方法以键值对(key-value)的方式保存数据,或者remove移除某个键(key),及调用clear方法删除所有内容。最后需要调用commit方法是使修改生效。
读取方式如下:
SharedPreferences settings = getSharedPreferences(STORE_NAME, MODE_PRIVATE);
int source = settings.getInt("sorceType", 1);
读取就更加简单了,只要获得SharedPreferences后,就可以通过getInt、getString等方法获取对应键(key)保存着的数据,如果没有找到key,则返回第二个参数作为默认值。
2.2 PreferenceActivity
上面介绍SharedPreferences键值对保存的基础知识,接着我们就可以来实现Android系统的设置界面。Android系统中和设置界面相关的包为android.preference,其中有一个继承了ListActivity的PreferenceActivity。
下面我们就先实现一个如下图2的最简单的PreferenceActivity设置界面,只有一个CheckBox选择选项,选中时显示“Yes,I love you!”,取消选择时显示“No,I am sorry.”。然后再介绍其具体的实现。
图2 PreferenceActivity界面
首先新建一个工程AndroidPreferenceDemo。
把extends Activity改为extends PreferenceActivity。
在res目录下新建一个xml文件夹,接着在这个文件夹下新建一个取名为preferences.xml的File文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Settings">
<CheckBoxPreference
android:title="Love me?"
android:summaryOn="Yes,I love you!"
android:summaryOff="No,I am sorry."
android:defaultValue="true">
</CheckBoxPreference>
</PreferenceScreen>
然后把java文件中的setContentView(R.layout.main);改为
addPreferencesFromResource(R.xml.preferences);
完成了,最后可以运行试下效果。
Java文件的实现很简单,继承PreferenceActivity后,调用其public void addPreferencesFromResource (int preferencesResId)
方法从一个xml文件中获取preference然后显示为标准的设置界面。
因此我们只要在xml文件中布局好要显示的设置界面内容就可以了。
下面我们看下布局文件xml的内容。
对应的xml中可以使用的标签(Tag)可以分为两类,一类是管理布局的显示,如PreferenceScreen;另一类是具体的设置内容,如CheckBoxPreference。
PreferenceScreen可以显示一个完整的页面,可以嵌套,包含在PreferenceScreen标签里的内容都将以一个完整的页面显示。
我们学习一个PreferenceScreen的界面,如下图3所示。
图3 PreferenceScreen相关的布局
当点击左图的选项时,分别会以新的页面显示,如中间和右图所示。
上面页面对应的xml布局文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Settings">
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Emotions"
android:summary="settings about emotions">
<CheckBoxPreference
android:title="Love me?"
android:summaryOn="Yes,I love you!"
android:summaryOff="No,I am sorry."
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:title="Hate me?"
android:summaryOn="Yes,I hate you!"
android:summaryOff="No,you are a good person."
android:defaultValue="false">
</CheckBoxPreference>
</PreferenceScreen>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Relations"
android:summary="settings about relations">
<CheckBoxPreference
android:title="Family?"
android:summaryOn="Yes,we are family!"
android:summaryOff="No,I am sorry."
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:title="Friends?"
android:summaryOn="Yes,we are friends!"
android:summaryOff="No,I am sorry."
android:defaultValue="false">
</CheckBoxPreference>
</PreferenceScreen>
</PreferenceScreen>
和管理布局显示相关的除了PreferenceScreen外,还有一个常用的为PreferenceCategory,我们把上面xml文件中间那两个PreferenceScreen改为PreferenceCategory就可以看到PreferenceCategory的显示效果了,如下图4所示。
图4 PreferenceCategory相关的布局
可以看到PreferenceCategory是把包含的内容归为同一类,但只是显示在一个页面中。
管理布局相关的两个标签可以使设置内容显示的更有条理,但是更主要的还是具体设置相关的标签,除了我们已经在使用的用于二选一的CheckBoxPreference外,Android系统提供的还有以对话框显示的DialogPreference,可以输入文本的EditTextPreference,以列表方式显示供选择的ListPreference和设置铃声用的RingtonePreference。
下面我们选择比较常用的ListPreference来介绍下具体使用。
首先在res/values/文件夹下新建一个取名为array.xml的文件,内容为:
然后在res/values/strings.xml文件中添加
需要的数据现在准备好了,下面我们来完成对应的xml文件。xml文件中各个标签的属性比较多,虽然Android有代码自动补全功能,但是还是使用Android提供的Structure界面来填写比较方便,下面我们就以这个方式来完成。
在res/xml文件夹下新建一个Android XML File文件,取名为preferencesii.xml,类型选择Preference,Root Element选择PreferenceScreen。 在Structure方式显示时,就会列出选中标签的所有属性,然后就可以根据需要填写属性,如下图5所示:
图5 填写xml中Preference的属性
从上图我们可以看到一个Preference拥有的属性。其中Key为这个Preference的ID,设置了才可以在代码中引用,Title是显示的标题,Summary是显示在标题下的文字介绍, 一般在Dependency中填写一个CheckBoxPreference的Key,这样就会在填写的那个CheckBoxPreference勾选时当前这个Preference才可用,Default Value为初始值,等等。
点击“Add”按钮,就会添加新的标签,我们依次添加一个CheckBoxPreference和ListPreference。属于CheckBoxPreference的特有属性主要为Summary On和Summary Off,比较好理解。下面具体来看下ListPreference属性的填写:
图6 ListPreference的属性
我们可以看到,ListPreference除了继承自Preference的属性外,还有自己ListPreference的属性和继承自DialogPreference的属性。其中属于ListPreference的属性有两个:Entries填的为一个字符串数组,是列表显示出来的值,而Entry Values是长度对应的字符串数组,是和Entries对应的具体的值。DialogPreference只要填一个Dialog title标题和一个取消按钮显示的字即可。在Preference属性的Dependency中我们填写上面一个CheckBoxPreference的Key,这样就会只有在CheckBoxPreference勾选时这个ListPreference才有效。
图7 完整的xml显示
最后把java文件中的addPreferencesFromResource(R.xml.preferences);改为addPreferencesFromResource(R.xml.preferencesii);
保存运行,看下效果。
图8 ListPreference显示
2.3 OnPreferenceChangeListener
以上我们分别介绍了Preference对数据的保存及PreferenceActivity设置界面。当PreferenceActivity中的内容改变时,Android系统会自动进行保存和持久化维护,我们只需要在要用的设置界面中数据的地方进行读取就可以了。同时Android还提供了OnPreferenceClickListener和OnPreferenceChangeListener两个与Preference相关的监听接口,当PreferenceActivity中的某一个Preference进行了点击或者改变的操作时,都会回调接口中的函数,这样可以第一个时间向其他Activity等通知系统设置进行了改变。
下面我们以一个具体的Demo说明PreferenceActivity和其监听接口的使用。
新建一个工程AndroidPreferenceDemoII,并按上面的步骤添加xml文件夹和其内容Preferenceii.xml,还有values文件夹中的array.xml和strings.xml。
新建一个名为Settings的class,内容为:
//继承PreferenceActivity,并实现OnPreferenceChangeListener和OnPreferenceClickListener监听接口
public class Settings extends PreferenceActivity implements OnPreferenceChangeListener,
OnPreferenceClickListener{
//定义相关变量
String updateSwitchKey;
String updateFrequencyKey;
CheckBoxPreference updateSwitchCheckPref;
ListPreference updateFrequencyListPref;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//从xml文件中添加Preference项
addPreferencesFromResource(R.xml.preferencesii);
//获取各个Preference
updateSwitchKey = getResources().getString(R.string.auto_update_switch_key);
updateFrequencyKey = getResources().getString(R.string.auto_update_frequency_key);
updateSwitchCheckPref = (CheckBoxPreference)findPreference(updateSwitchKey);
updateFrequencyListPref = (ListPreference)findPreference(updateFrequencyKey);
//为各个Preference注册监听接口
updateSwitchCheckPref.setOnPreferenceChangeListener(this);
updateSwitchCheckPref.setOnPreferenceClickListener(this);
updateFrequencyListPref.setOnPreferenceChangeListener(this);
updateFrequencyListPref.setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// TODO Auto-generated method stub
Log.v("SystemSetting", "preference is changed");
Log.v("Key_SystemSetting", preference.getKey());
//判断是哪个Preference改变了
if(preference.getKey().equals(updateSwitchKey))
{
Log.v("SystemSetting", "checkbox preference is changed");
}
else if(preference.getKey().equals(updateFrequencyKey))
{
Log.v("SystemSetting", "list preference is changed");
}
else
{
//如果返回false表示不允许被改变
return false;
}
//返回true表示允许改变
return true;
}
@Override
public boolean onPreferenceClick(Preference preference) {
// TODO Auto-generated method stub
Log.v("SystemSetting", "preference is clicked");
Log.v("Key_SystemSetting", preference.getKey());
//判断是哪个Preference被点击了
if(preference.getKey().equals(updateSwitchKey))
{
Log.v("SystemSetting", "checkbox preference is clicked");
}
else if(preference.getKey().equals(updateFrequencyKey))
{
Log.v("SystemSetting", "list preference is clicked");
}
else
{
return false;
}
return true;
}
}
主要是获取xml文件中的各个Preference,然后为其注册监听接口,最后在监听接口的回调函数中打印相关的信息。
接着在Manifest文件中对这个Activity进行注册:
<activity android:name=".Settings">
</activity>
然后对AndroidPreferenceDemoII.java文件进行如下修改:
public class AndroidPreferenceDemoII extends Activity {
/** Called when the activity is first created. */
// 菜单项
final private int menuSettings=Menu.FIRST;
private static final int REQ_SYSTEM_SETTINGS = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
//创建菜单
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// 建立菜单
menu.add(0, menuSettings, 2, "设置");
return super.onCreateOptionsMenu(menu);
}
//菜单选择事件处理
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item)
{
switch (item.getItemId())
{
case menuSettings:
//转到Settings设置界面
startActivityForResult(new Intent(this, Settings.class), REQ_SYSTEM_SETTINGS);
break;
default:
break;
}
return super.onMenuItemSelected(featureId, item);
}
//Settings设置界面返回的结果
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQ_SYSTEM_SETTINGS)
{
//获取设置界面PreferenceActivity中各个Preference的值
String updateSwitchKey = getResources().getString(R.string.auto_update_switch_key);
String updateFrequencyKey = getResources().getString(R.string.auto_update_frequency_key);
//取得属于整个应用程序的SharedPreferences
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
Boolean updateSwitch = settings.getBoolean(updateSwitchKey, true);
String updateFrequency = settings.getString(updateFrequencyKey, "10");
//打印结果
Log.v("CheckBoxPreference_Main", updateSwitch.toString());
Log.v("ListPreference_Main", updateFrequency);
}
else
{
//其他Intent返回的结果
}
}
}
主要是添加一个设置菜单,点击后转到设置界面,当从设置界面返回后读取保存的内容,并打印出来查看。
最后我们看一下运行的效果:
图9 设置界面操作
操作的Log输出如下图10所示:
图10 DDMS的LogCat输出
点击设置菜单转到设置界面后,首先点击CheckBox,输出前6行,然后点击ListPreference,输出3行,当选择列表的第二个进行改变时,再输出3行,最后按返回键回到第一个页面,打印出最后2行。
最后我们看一下这些设置参数在系统中的保存文件。在DDMS的File Explorer中,查看data/data/com.ichliebephone文件夹,我们可以看到有如下文件:
图11 Preference保存的文件
把这个文件导出到电脑上,可以看到起内容为:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="auto_update_frequency">30</string>
<boolean name="auto_update_switch" value="true" />
</map>
文件以map的方式保存了两个设置参数
三.总结
我们学习了Android的Preference相关内容,包括最基本的SharedPreferences的使用,及进一步的PreferenceActivity设置界面的介绍,最后根据以上的内容完成了一个简单的Demo。
Preference键值对的方式是Android最简单的一种数据持久化方式,虽然比较简单,但是也比较实用,特别是在保存小量的数据时。Android上的功能更加强大相对也更加复杂的SQLite数据保存方式我们以后接着学习。
文章对应的完整代码例子可以在这里下载: