对Android系统了解的都知道,Android系统有四种基本的数据保存方法,一是SharedPreference,二是文件,三是SQLite, 四是ContentProvider。看出来了吧,Preference,对就是使用SharedPreferneces以键值对的形式进行保存的。
2、SharedPreferneces
做软件开发应该都知道,很多软件会有配置文件,里面存放这程序运行当中的各个属性值,由于其配置信息并不多,如果采用数据库来存放并不划算,因为数据 库连接跟操作等耗时大大影响了程序的效率,因此我们使用键值这种一一对应的关系来存放这些配置信息。SharedPreferences正是 Android中用于实现这中存储方式的技术。
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、PreferencesActivity
在Android开发过程中我们有很大的机会需要用到参数设置功能,那么在Android应用中,我们如何实现参数设置界面及参数存储呢,下面我们来介绍一下Android中的一个特殊Activity–PreferencesActivity。
PreferencesActivity是Android中专门用来实现程序设置界面及参数存储的一个Activity。
2.1 创建PreferencesActivity
如何创建一个PreferenceActivity。 其实Eclipse提供了相应的创建工具,和创建Layout是基本相同的。步骤如下:
创建Android项目,并添加一个Android xml文件。注意,这次选择的不是Layout,而是Preference,而且注意Folder路径是 res/xml.
添加完成之后,在res/xml/下打开添加的preference.xml文件。可以看到Android也为我们提供两种编辑模式,可视化的结构设计及xml源码设计。推荐使用structure进行创建。如图所示:
2.2 PrefeneceActivity的基本组成
下面我们看看PrefeneceActivity都提供了哪几种元素可供使用。点击Add按钮,在打开的新窗口中可以看到以下几项:
CheckBoxPreference:CheckBox选择项,对应的值的ture或flase。如图:
EditTextPreference:输入编辑框,值为String类型,会弹出对话框供输入。
ListPreference: 列表选择,弹出对话框供选择。
Preference:只进行文本显示,需要与其他进行组合使用。
PreferenceCategory:用于分组。效果如下:
PreferenceScreen:PreferenceActivity的根元素,必须为它。
RingtonePreference:系统玲声选择。
OK,Preferenc的基本元素介绍完毕,下一节将使用它们创建一个完整的Preference并进行显示。
2.3 PreferenceActivity实例
分析MusicPlayer Setting,第一部分为“我的位置”,包括“使用无线网线”和“使用GPS”两个部分,而且都是CheckBox,根据上节学习,应该包括一个 PreferenceCategory和两个CheckBoxPreference。
Xml代码
<PreferenceCategory android:title="我的位置" android:key="set_local"> <CheckBoxPreference android:key="apply_wifi" android:summary="使用无线网络在应用程序(例如Google地图)中查看位置" android:title="使用无线网络" android:defaultValue="true"> </CheckBoxPreference> <CheckBoxPreference android:key="apply_gps" android:summary="定位到街道级别(需要消耗更多的电量以及天气允许)" android:title="使用GPS"> </CheckBoxPreference> </PreferenceCategory>以上代码当然也可以用Android提供的IDE工具直接生成。视频结构如下:
PreferenceCategory属性分析:
title:显示的标题
key:唯一标识(至少在同一程序中是唯一),SharedPreferences也将通过此Key值进行数据保存,也可以通过key值获取保存的信息 (以下相同)。
CheckBoxPreference属性分析:
Key:唯一标识.
title:显示标题(大字体显示)
summary:副标题(小字体显示)
defaultValue:默认值(当然,此处只能是true或false了)
Preference.xml的第二部分为“无线和网络设置”,此部分包括的内容比较多,也稍微复杂,一步一步来分析。
xml代码:
<PreferenceCategory android:title="无线和网络设置"> <CheckBoxPreference android:key="apply_fly" android:summary="禁用所有无线连接" android:title="飞行模式"> </CheckBoxPreference> <CheckBoxPreference android:key="apply_internet" android:summary="禁用通过USB共享Internet连接" android:title="Internet共享"> </CheckBoxPreference> <CheckBoxPreference android:key="apply_wifi" android:summary="打开Wi-Fi" android:title="Wi-Fi"> </CheckBoxPreference> <Preference android:summary="设置和管理无线接入点" android:title="Wi-Fi设置" android:dependency="apply_wifi" android:key="wifi_setting"> </Preference> <CheckBoxPreference android:key="apply_bluetooth" android:summary="启用蓝牙" android:title="蓝牙"> </CheckBoxPreference> <Preference android:summary="管理连接、设备设备名称和可检测性" android:title="蓝牙设置" android:dependency="apply_bluetooth" android:key="bluetooth_setting"> </Preference> <EditTextPreference android:key="number_edit" android:title="输入电话号码"> </EditTextPreference> <ListPreference android:title="部门设置" android:entries="@array/department" android:entryValues="@array/department_value" android:dialogTitle="选择部门" android:key="depart_value"> </ListPreference> <RingtonePreference android:ringtoneType="all" android:title="玲聲" android:showDefault="true" android:key="ring_key" android:showSilent="true"> </RingtonePreference> </PreferenceCategory>对应的Structure图:
第二部分中前三个都为CheckBoxPreference,不心多说,从<Preference android:key="bluetooth_setting"/>开始。
Preference属性分析:
Key:唯一标识.
title:显示标题(大字体显示)
summary:副标题(小字体显示)
dependency:附属(嘛意思),即标识此元素附属于某一个元素(通常为CheckBoxPreference),dependency值为所附 属元素的key。上面代码中的Preference元素附属于key等于“apply_bluetooth”的CheckPreference元素,当 CheckPreference值为true时,Preference则为可用,否则为不可用。
EditTextPreperence属性分析:
Key:唯一标识.
title:显示标题(大字体显示)
ListPreference属性分析:
Key:唯一标识.
title:显示标题(大字体显示)
dialogTitle:弹出对话框的标题
entries:列表中显示的值。为一个数组,通读通过资源文件进行设置。
entryValues:列表中实际保存的值,也entries对应。为一个数组,通读通过资源文件进行设置。以下代码显示的是arrays.xml文件中内容:
<resources> <string-array name="department"> <item>综合部</item> <item>行政部</item> <item>外贸部</item> </string-array> <string-array name="department_value"> <item>001</item> <item>002</item> <item>003</item> </string-array> </resources>
点击“Add”按钮,就会添加新的标签,我们依次添加一个CheckBoxPreference和ListPreference。属于 CheckBoxPreference的特有属性主要为Summary On和Summary Off,比较好理解。下面具体来看下ListPreference属性的填写:
我们可以看到,ListPreference除了继承自Preference的属性外,还有自己ListPreference的属性和继承自 DialogPreference的属性。其中属于ListPreference的属性有两个:Entries填的为一个字符串数组,是列表显示出来的 值,而Entry Values是长度对应的字符串数组,是和Entries对应的具体的值。DialogPreference只要填一个Dialog title标题和一个取消按钮显示的字即可。在Preference属性的Dependency中我们填写上面一个CheckBoxPreference 的Key,这样就会只有在CheckBoxPreference勾选时这个ListPreference才有效。
最后把java文件中的addPreferencesFromResource(R.xml.preferences);改为addPreferencesFromResource(R.xml.preferencesii);
保存运行,看下效果。
OK,Preference.xml内容已经分析完毕,属性都大致相同,相信亲自动力一试也就那么回事。那么如何把Preference.xml中内容展现出来呢?
Layout是通过继续自Activity的类来进行显示的,前面提到过,PreferenceActivity是专门用于显示preference的,所以只要创建一个继承自PreferenceActivity类即可。代码如下:
public class Setting extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 所的的值将会自动保存到SharePreferences addPreferencesFromResource(R.xml.preference); } }接下来就是运行程序,显示劳动成果。至此,工作已经完成大半,所有的值都会保存到SharedPreferences中,我们也可以读取到保存的结果。
2.4 如何获取ListPreference中选中的值
研究了一天,貌似ListPreference中根本就没有什么回调函数可以用
于是,向上一层,把注意力集中于SharedPreferences,
发现有这么个接口:onSharedPreferenceChanged
抱着试试看的态度,实现了该接口,发现此方法可行,先将部分代码分享如下:
package yinger.firewall; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; public class CallFireWall extends PreferenceActivity implements OnSharedPreferenceChangeListener { ListPreference lp = null; String whiteMode = "1"; String blackMode = "2"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); this.setTitle("Ying_er" + " Call Safe"); SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(this); prefs.registerOnSharedPreferenceChangeListener(this); lp = (ListPreference) findPreference(getString(R.string.pref_mode_key)); } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { // TODO Auto-generated method stub return super.onPreferenceTreeClick(preferenceScreen, preference); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { // TODO Auto-generated method stub System.out.println("onSharedPreferenceChanged"); if (key == getString(R.string.pref_mode_key)){ System.out.println(lp.getValue()); if(lp.getValue().equals(whiteMode)){ } } } }注意:该事件需要注册。既:
prefs.registerOnSharedPreferenceChangeListener(this);
实际上,使用如下方法也是可以得到值的:
PreferenceManager.getDefaultSharedPreferences(context).getBoolean(R.string.pref_mode_key, DEFAULT);
2.5 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; }
</pre><p> 当然重写PreferenceActivity的 onPreferenceTreeClick的方法就可以了,通过参数preference来判断是对那一个元素进行的,并根据需要进行操作。</p><p> </p><pre name="code" readonly="readonly">@Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { return false; }
2.5 OnPreferenceChangeListener与OnPreferenceClickListener的区别
两种监听器OnPreferenceChangeListener和 OnPreferenceClickListener,OnPreferenceClickListener的意思好理解,但是 OnPreferenceChangeListener就不好懂了,所以就写了个Demo分析了一下,代码如下:
</pre><p> </p><pre name="code">package com.luther.test; import com.luther.test.R; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.widget.Toast; public class PreferenceDemoActivity extends PreferenceActivity implements OnPreferenceChangeListener, OnPreferenceClickListener{ private ListPreference mList1Prefs; private ListPreference mList2Prefs; private CheckBoxPreference mCheckPrefs; private EditTextPreference mEditPrefs; private SharedPreferences mSharedPrefs; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preference); initViews(); } private void initViews(){ //得到以包命名的SharedPreferences mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); mList1Prefs = (ListPreference) findPreference("key_call_config"); mList2Prefs = (ListPreference) findPreference("key_msg_config"); mCheckPrefs = (CheckBoxPreference) findPreference("switch"); mEditPrefs = (EditTextPreference) findPreference("autoreply_text_cpntent"); mList1Prefs.setOnPreferenceClickListener(this); mList1Prefs.setOnPreferenceChangeListener(this); mList2Prefs.setOnPreferenceChangeListener(this); mList2Prefs.setOnPreferenceClickListener(this); } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { if(preference == mEditPrefs){ String toastStr = mEditPrefs.getTitle() + "\n" + "Content: " + mEditPrefs.getText(); showToast(toastStr); } return super.onPreferenceTreeClick(preferenceScreen, preference); } public boolean onPreferenceClick(Preference preference) { String prefsValue = mSharedPrefs.getString(preference.getKey(), "-1"); showToast(prefsValue); return false; } public boolean onPreferenceChange(Preference preference, Object newValue) { if(mList1Prefs == preference){ String prefsValue = mSharedPrefs.getString(preference.getKey(), "-1"); showToast(prefsValue); } if(mList2Prefs == preference){ String prefsValue = newValue.toString(); showToast(prefsValue); mList2Prefs.setValue(prefsValue); } return false; } private void showToast(String arg){ Toast.makeText(this, arg, Toast.LENGTH_SHORT).show(); } }程序原理:用一个ListPreference分别注册这两个监听器,然后用Tosat看他们在什么时候响应。
.../res/xml/preference文件如下:
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="@string/settings"> <PreferenceCategory android:title="@string/general_settings"> <ListPreference android:key="key_call_config" android:title="@string/incoming_call_come" android:dialogTitle="@string/incoming_call_come" android:entries="@array/response_entries" android:entryValues="@array/response_entry_values"/> <ListPreference android:key="key_msg_config" android:title="@string/message_come" android:dialogTitle="@string/message_come" android:entries="@array/response_entries" android:entryValues="@array/response_entry_values"/> </PreferenceCategory> <PreferenceCategory android:title="@string/user_defined_autoreply_settings"> <CheckBoxPreference android:title="@string/autoreply_switch" android:key="switch"/> <EditTextPreference android:title="@string/user_defined_autoreply_text" android:key="autoreply_text_cpntent" android:dependency="switch" android:dialogTitle="@string/user_defined_autoreply_text" android:positiveButtonText="@android:string/ok" android:negativeButtonText="@android:string/cancel"/> </PreferenceCategory> </PreferenceScreen>以下是ListPreference的Entries和EntryValues:
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="response_entries"> <item>静音</item> <item>振动</item> <item>正常响铃</item> <item>LED灯亮</item> </string-array> <string-array name="response_entry_values"> <item>0</item> <item>1</item> <item>2</item> <item>3</item> </string-array> </resources>string.xml文件:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World, PreferenceDemoActivity!</string> <string name="app_name">PreferenceDemo</string> <string name="settings">设置</string> <string name="general_settings">常规设置</string> <string name="incoming_call_come">来电时</string> <string name="message_come">来短信时</string> <string name="user_defined_autoreply_settings">自定义回复设置</string> <string name="autoreply_switch">自动回复</string> <string name="user_defined_autoreply_text">自动回复短信</string> </resources>下面是Demo的主界面:
编译好,运行程序:点击mList1Prefs(来电时),Toast显示“-1”。再点击一个选项,对话框消失,Toast显示“-1”,为什么会这样?为什么不是第一次选中时的选项值?
其实,这是因为设置了OnPreferenceChangeListener。默认情况下,普通的ListPreference第一次选中一个值之后,Android框架会存贮好选中的值,下次打开,会默认选中上次选中的值,但是设置OnPreferenceChangeListener之后,值的存储就要自己来了,详见代码中的mList2Prefs.setValue();“来短信时”这一项就能够正常存贮选中的值。
现在区别就出来了:
OnPreferenceClickListener:是响应点击preference的事件,比如程序中,一点ListPreference,onPreferenceClick()中就弹出Toast。
OnPreferenceChangeListener:是响应preference的值被改变的事件(此时被改变的值需要自己存贮到SharedPreferences) ,比如程序中,点击ListPreference的某一项,onPreferenceChange()中就会弹出Toast。
另外:onPreferenceTreeClick()这个方法感觉和OnPreferenceClickListener的回调方法onPreferenceClick()的作用差不多,一样是响应点击的事件。
回头再看OnPreferenceChangeListener的文档:
Interface definition for a callback to be invoked when the value of this Preference has been changed by the user and is about to be set and/or persisted. This gives the client a chance to prevent setting and/or persisting the value.
就非常明白了~