Android Preference 须知
一、理论
1.前言
在Android的应用开发中经常会涉及到设置界面的设计开发,为此Android提供了名为Preference的设置处理机制,沿用这种机制的话能省去开发者很多不必要的时间开支。
那Preference究竟是什么呢?看一下你Android手机里的“设置”这个应用就知道了,大体界面如下:
这个设置界面就是通过Preference构建的,可能有些同学会说,这不就是一个普通的LinearLayout吗?有什么特别的?下面我们就开具体说说什么是Preference。
2.什么是Preference?
Preference翻译过来是“偏爱,爱好”的意思,在Android中Preference是一个具体的类,它代表着一个最基本的UI块(可以理解为ListView中的一个item),并且显示在Activity或Fragment内置的ListView上面,如图1-1中的“声音”选项,就是一个Preference的UI块表现(这里为什么要说表现呢?因为Preference本身并不是继承View的视图类,它只是代表着一个偏好选项然后通过提供UI块来展示这些偏好选项)。并且每一个UI块都会和一个SharePreferences关联来保存或恢复偏好设置。
3.Preference从何而来?
得到Preference的方法有两种:一种是从Xml文件中获取,在Xml文件中每一个节点都能指定特定的Preference的子类。另外一种方法就是在代码中动态的创建。为了处理好Preference与SharePreferences的关联,Preference类提供了一个Key来作为使用SharePreferences时的Key。
4.Preference的子类们
开关类型:
TwoStatePreference是一个抽象类,它下面的两个子类CheckBoxPreference和SwitchPreference。
弹出框类型:
弹出框类型的都继承子虚拟类DialogPreference,分别是EditPreference,MultiCheckPreference,ListPreference,MultiSelectListPreference,SeekBarDialogPreference,VolumePreferenc。
特殊:
RingtonePreference和SeekBarPreference分别是铃声选择和滑块。
组类型:
组类型都继承子抽象类PreferenceGroup,其中PreferenceCategory表示分类,PreferenceScreen主要用来表示一个设置界面中的根节点。
5.Preference其他相关类介绍:
GenericInflater:用于解析xml文件
PreferenceInflater:继承自GenericInflater用于解析包含Preference的xml
PreferenceActivity:在kk和之前的版本上用于显示一系列Preference的Header(标头,用于跳转到对应的PreferenceFragment),从L开始PreferenceActivity的功能被PreferenceFragment集成了。
PreferenceFragment:用ListView来显示一系列Preference的层次结构。
PreferenceManager:用于帮助从xml或代码中创建Preference的层次结构。
二、实践
1.从xml文件中构建Preference ,主要代码如下:
----PreferencesFromXml.java----
- @Override
- protected void onCreate(BundlesavedInstanceState) {
- super.onCreate(savedInstanceState);
- // Load the preferences from an XMLresource
- //这个方法在L上已经不被提倡使用
- addPreferencesFromResource(R.xml.preferences);
- }
preferences文件内容如下:
- <?xml version="1.0" encoding="utf-8"?>
- <!--BEGIN_INCLUDE(preferences) -->
- <PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android">
- <PreferenceCategory
- android:title="@string/inline_preferences">
- <CheckBoxPreference
- android:key="checkbox_preference"
- android:title="@string/title_checkbox_preference"
- android:summary="@string/summary_checkbox_preference" />
- </PreferenceCategory>
- <PreferenceCategory
- android:title="@string/dialog_based_preferences">
- <EditTextPreference
- android:key="edittext_preference"
- android:title="@string/title_edittext_preference"
- android:summary="@string/summary_edittext_preference"
- android:dialogTitle="@string/dialog_title_edittext_preference" />
- <ListPreference
- android:key="list_preference"
- android:title="@string/title_list_preference"
- android:summary="@string/summary_list_preference"
- android:entries="@array/entries_list_preference"
- android:entryValues="@array/entryvalues_list_preference"
- android:dialogTitle="@string/dialog_title_list_preference" />
- </PreferenceCategory>
- <PreferenceCategory
- android:title="@string/launch_preferences">
- <!-- This PreferenceScreen tag servesas a screen break (similar to page break
- in word processing). Like forother preference types, we assign a key
- here so it is able to save andrestore its instance state. -->
- <PreferenceScreen
- android:key="screen_preference"
- android:title="@string/title_screen_preference"
- android:summary="@string/summary_screen_preference">
- <!-- You can place morepreferences here that will be shown on the next screen. -->
- <CheckBoxPreference
- android:key="next_screen_checkbox_preference"
- android:title="@string/title_next_screen_toggle_preference"
- android:summary="@string/summary_next_screen_toggle_preference" />
- </PreferenceScreen>
- <PreferenceScreen
- android:title="@string/title_intent_preference"
- android:summary="@string/summary_intent_preference">
- <intent android:action="android.intent.action.VIEW"
- android:data="http://www.android.com" />
- </PreferenceScreen>
- </PreferenceCategory>
- <PreferenceCategory
- android:title="@string/preference_attributes">
- <CheckBoxPreference
- android:key="parent_checkbox_preference"
- android:title="@string/title_parent_preference"
- android:summary="@string/summary_parent_preference" />
- <!-- The visual style of a childis defined by this styled theme attribute. -->
- <CheckBoxPreference
- android:key="child_checkbox_preference"
- android:dependency="parent_checkbox_preference"
- android:layout="?android:attr/preferenceLayoutChild"
- android:title="@string/title_child_preference"
- android:summary="@string/summary_child_preference" />
- </PreferenceCategory>
- </PreferenceScreen>
- <!-- END_INCLUDE(preferences)-->
就是这么简单,只需要简单配置一下xml文件,一个设置界面就完成了,上面的xml文件对应的界面如下:
我们来分析一下这个xml文件
首先看它的根节点 PreferenceScreen 它对应的就是PreferenceScreen对象表示这一层次结构的最外层。接着是PreferenceCategory它表示一个分类的开始对应PreferenceCategory对象。在PreferenceCategory里可以看到一些节点如:CheckBoxPreference,EditTextPreference,ListPreference等,都是表示一个设置选项,其对应的也是名字与之相同的类。
2.从代码中构建Preference
代码如下:
----PreferencesFromCode.java----
- public classPreferencesFromCode extends PreferenceActivity {
- private static final StringPARENT_CHECKBOX_PREFERENCE = "parent_checkbox_preference";
- @Override
- protected void onCreate(BundlesavedInstanceState) {
- super.onCreate(savedInstanceState);
- PreferenceScreen root =getPreferenceManager().createPreferenceScreen(this);
- setPreferenceScreen(root);
- populatePreferenceHierarchy(root);
- }
- private voidpopulatePreferenceHierarchy(PreferenceScreen root) {
- // Inline preferences
- PreferenceCategory inlinePrefCat = newPreferenceCategory(this);
- inlinePrefCat.setTitle(R.string.inline_preferences);
- root.addPreference(inlinePrefCat);
- // Checkbox preference
- CheckBoxPreference checkboxPref = newCheckBoxPreference(this);
- checkboxPref.setKey("checkbox_preference");
- checkboxPref.setTitle(R.string.title_checkbox_preference);
- checkboxPref.setSummary(R.string.summary_checkbox_preference);
- inlinePrefCat.addPreference(checkboxPref);
- // Switch preference
- SwitchPreference switchPref = newSwitchPreference(this);
- switchPref.setKey("switch_preference");
- switchPref.setTitle(R.string.title_switch_preference);
- switchPref.setSummary(R.string.summary_switch_preference);
- inlinePrefCat.addPreference(switchPref);
- // Dialog based preferences
- PreferenceCategory dialogBasedPrefCat =new PreferenceCategory(this);
- dialogBasedPrefCat.setTitle(R.string.dialog_based_preferences);
- root.addPreference(dialogBasedPrefCat);
- // Edit text preference
- EditTextPreference editTextPref = newEditTextPreference(this);
- editTextPref.setDialogTitle(R.string.dialog_title_edittext_preference);
- editTextPref.setKey("edittext_preference");
- editTextPref.setTitle(R.string.title_edittext_preference);
- editTextPref.setSummary(R.string.summary_edittext_preference);
- dialogBasedPrefCat.addPreference(editTextPref);
- // List preference
- ListPreference listPref = newListPreference(this);
- listPref.setEntries(R.array.entries_list_preference);
- listPref.setEntryValues(R.array.entryvalues_list_preference);
- listPref.setDialogTitle(R.string.dialog_title_list_preference);
- listPref.setKey("list_preference");
- listPref.setTitle(R.string.title_list_preference);
- listPref.setSummary(R.string.summary_list_preference);
- dialogBasedPrefCat.addPreference(listPref);
- // Launch preferences
- PreferenceCategory launchPrefCat = newPreferenceCategory(this);
- launchPrefCat.setTitle(R.string.launch_preferences);
- root.addPreference(launchPrefCat);
- /*
- * The Preferences screenPref serves asa screen break (similar to page
- * break in word processing). Like forother preference types, we assign
- * a key here so that it is able tosave and restore its instance state.
- */
- // Screen preference
- PreferenceScreen screenPref =getPreferenceManager().createPreferenceScreen(this);
- screenPref.setKey("screen_preference");
- screenPref.setTitle(R.string.title_screen_preference);
- screenPref.setSummary(R.string.summary_screen_preference);
- launchPrefCat.addPreference(screenPref);
- /*
- * You can add more preferences toscreenPref that will be shown on the
- * next screen.
- */
- // Example of next screen togglepreference
- CheckBoxPreferencenextScreenCheckBoxPref = new CheckBoxPreference(this);
- nextScreenCheckBoxPref.setKey("next_screen_toggle_preference");
- nextScreenCheckBoxPref.setTitle(R.string.title_next_screen_toggle_preference);
- nextScreenCheckBoxPref.setSummary(R.string.summary_next_screen_toggle_preference);
- screenPref.addPreference(nextScreenCheckBoxPref);
- // Intent preference
- PreferenceScreen intentPref =getPreferenceManager().createPreferenceScreen(this);
- intentPref.setIntent(newIntent().setAction(Intent.ACTION_VIEW)
- .setData(Uri.parse("http://www.android.com")));
- intentPref.setTitle(R.string.title_intent_preference);
- intentPref.setSummary(R.string.summary_intent_preference);
- launchPrefCat.addPreference(intentPref);
- // Preference attributes
- PreferenceCategory prefAttrsCat = newPreferenceCategory(this);
- prefAttrsCat.setTitle(R.string.preference_attributes);
- root.addPreference(prefAttrsCat);
- // Visual parent toggle preference
- CheckBoxPreference parentCheckBoxPref =new CheckBoxPreference(this);
- parentCheckBoxPref.setTitle(R.string.title_parent_preference);
- parentCheckBoxPref.setSummary(R.string.summary_parent_preference);
- prefAttrsCat.addPreference(parentCheckBoxPref);
- parentCheckBoxPref.setKey(PARENT_CHECKBOX_PREFERENCE);
- // Visual child toggle preference
- // See res/values/attrs.xml for the<declare-styleable> that defines
- // TogglePrefAttrs.
- TypedArray a = obtainStyledAttributes(R.styleable.TogglePrefAttrs);
- CheckBoxPreference childCheckBoxPref =new CheckBoxPreference(this);
- childCheckBoxPref.setTitle(R.string.title_child_preference);
- childCheckBoxPref.setSummary(R.string.summary_child_preference);
- childCheckBoxPref.setLayoutResource(
- a.getResourceId(R.styleable.TogglePrefAttrs_android_preferenceLayoutChild,
- 0));
- prefAttrsCat.addPreference(childCheckBoxPref);
- childCheckBoxPref.setDependency(PARENT_CHECKBOX_PREFERENCE);
- a.recycle();
- }
- }
动态构建Preference的过程是:构建PreferenceScreen,然后构建PreferenceCategory,并往PreferenceCategory中添加需要使用的Preference的子类,然后将PreferenceCategory添加到PreferenceScreen。
上述代码效果图如下:
三、Preference实现机制
在讲机制之前我们来做一个简单的demo,通过这个demo来直击Preference的运行现场。
demo很简单,有两个界面:
MyPreferenceDemo.java (一个activity,显示从SharePreferences取key为my_demo_key的值)
MySetting.java (一个key为my_demo_key的EditPreference)
关键代码如下:
----MyPreferenceDemo.java
- public class MyPreferenceDemo extends Activity {
- private TextView mTextView ;
- @Override
- protected void onCreate(BundlesavedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mTextView = (TextView)findViewById(R.id.show_preferecne) ;
- }
- @Override
- protected void onResume() {
- super.onResume();
- mTextView.setText(getPreferenceByKey());
- }
- private String getPreferenceByKey() {
- String pref = "" ;
- pref =PreferenceManager.getDefaultSharedPreferences(this).getString("my_demo_key","Hello") ;
- return pref;
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu){
- // Inflate the menu; this adds itemsto the action bar if it is present.
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
- @Override
- public boolean onOptionsItemSelected(MenuItemitem) {
- if(item.getItemId() == R.id.action_settings){
- Toast.makeText(this, "settings", Toast.LENGTH_LONG).show() ;
- Intent intent = new Intent() ;
- intent.setClass(this, MySetting.class) ;
- startActivity(intent) ;
- }
- return super.onOptionsItemSelected(item);
- }
- }
----MySetting.java
- public class MySetting extends PreferenceActivity {
- @Override
- protected void onCreate(BundlesavedInstanceState) {
- super.onCreate(savedInstanceState);
- PreferenceScreen root = getPreferenceManager().createPreferenceScreen(this);
- setPreferenceScreen(root);
- populatePreferenceHierarchy(root);
- }
- private void populatePreferenceHierarchy(PreferenceScreenroot) {
- PreferenceCategory myCat = new PreferenceCategory(this);
- myCat.setTitle("My PreferenceDemo");
- root.addPreference(myCat);
- EditTextPreference editTextPref = new EditTextPreference(this);
- editTextPref.setDialogTitle("please inputsomething");
- editTextPref.setKey("my_demo_key");
- editTextPref.setTitle("settings");
- editTextPref.setSummary("设置activity中需要显示的字符串");
- myCat.addPreference(editTextPref);
- }
- }
程序界面如下:
点击“settings”输入“这是preference的demo”,然后返回第一个界面,就会显示 “这是preference的demo”。
我们先来看看获得SharePreferences的这个方法:
- PreferenceManager.getDefaultSharedPreferences(this)
跟进源代码发现getDefaultSharedPreferences的实现如下:
- public static SharedPreferencesgetDefaultSharedPreferences(Context context) {
- returncontext.getSharedPreferences(getDefaultSharedPreferencesName(context),
- getDefaultSharedPreferencesMode());
- }
- private static StringgetDefaultSharedPreferencesName(Context context) {
- return context.getPackageName() + "_preferences";
- }
- private static intgetDefaultSharedPreferencesMode() {
- return Context.MODE_PRIVATE;
- }
内部其实还是调用了 getSharedPreferences这个方法来获得SharePreferences,参数name和mode分别是:context.getPackageName() +"_preferences"和 Context.MODE_PRIVATE。
接着我们看看我们使用的 EditTextPreference是怎么将字符串保存到SharePreferences的,
我们看到在EditTextPreference内部有这么一个回调方法:
- @Override
- protected void onDialogClosed(boolean positiveResult) {
- super.onDialogClosed(positiveResult);
- if (positiveResult) {
- String value = mEditText.getText().toString();
- if (callChangeListener(value)) {
- setText(value);
- }
- }
- }
当设置好字符串关闭对话框时这个方法会被回调,获得输入的字符串value然后调用settext(value),
- public void setText(String text) {
- final boolean wasBlocking =shouldDisableDependents();
- mText = text;
- persistString(text);
- final boolean isBlocking =shouldDisableDependents();
- if (isBlocking != wasBlocking) {
- notifyDependencyChange(isBlocking);
- }
- }
我们接着看persistString(text); 这个方法,首先persist的意思是持久化,那我们可以很容易联想到本地文件或者数据库之类的,那这边到底会把text保存到哪里?我们接着看。
- protected boolean persistString(String value) {
- if (shouldPersist()) {
- // Shouldn't store null
- if (value == getPersistedString(null)) {
- // It's already there, so the sameas persisting
- return true;
- }
- SharedPreferences.Editor editor = mPreferenceManager.getEditor();
- editor.putString(mKey, value);
- tryCommit(editor);
- return true;
- }
- return false;
- }
看到这里就就明了了,最终还是将value这个数据保存在了SharePreferences里面。
最基本的Preference实现机制就到这里了,总结一句话:
Preference设置属性的保存还是依赖于SharePreferences。
关于SharePreferences的运行机制,我会在后面在给出。
pdf下载 http://pan.baidu.com/s/1o6p2dYi
原文地址:http://blog.csdn.net/will_captain/article/details/40404523