PreferenceActivity相关
大多数Settings用的是PreferenceActivity?
为了说明这个问题,首先从需求说起。即:现有某一Activity专门用于手机属性设置,那么应该如何做呢?
根据我目前所学知识,会想到Activity + Preference 的组合,前者用于界面的构建,后者用于设置数据的存放。虽然想法是正确的,但是会比较繁琐,因为每个设置选项都要建立与其对应的Preference。
所以 现在有更好的选择了,那就是本文的主角:PreferenceActivity
从名字应该可以看出它其实 Activity 与 Perference 的混合物,它的神奇之处在于既可以充当设置界面,又可以很方便地保存设置数据。我们只需要新建一个继承自PreferenceActivity的Activity,然后在主程序中调用就可以了。这个PreferenceActivity中的设置存储是完全自动的,你不需要再用代码去实现设置的存储,PreferenceActivity创建后会自动创建一个配置文件/data/data/you_package_name/shared_prefs/you_package_name_you_xml_name.xml。
如何操作PreferenceActivity?
先从需求说起,常见的手机属性设置都有哪些种类:
1、CheckBox 用于【确定/取消】 某项功能(如:是否静音),对应于:CheckBoxPreference 其在Preference 有一个选项与其自动绑定 下同
2、ListView 用于列出所有选择(如:关于设备),对应于:ListPreference
3、TextView 纯粹为了说明状态(如:信号强度),对应于:Preference
4、Switch 用于【开启/关闭】某项功能(如:Motion),对应于SwitchPreferenceScreen
5、EditText 用于在某些特定的情况下写入数据(如:个人信息),对应于EditTextPreference
6、特殊的preference,如:铃音选择,系统会把这个选项自动绑定到铃音数据上,不用设置其他就可以达到切换铃音的效果,对应于RingtonePreference
操作PreferenceActivity步骤:
- 1. PreferenceActivity界面比较特别在 res/xml/setting.xml中
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
PreferenceScreen:设置页面,可嵌套形成二级设置页面,用Title参数设置标题。 |
<PreferenceCategory android:title="SMS 助手" >
<CheckBoxPreference
android:defaultValue="false"
PreferenceCategory:某一类相关的设置,可用Title参数设置标题。 |
android:key="smsSilence"
android:summaryOff="关闭"
android:summaryOn="开启"
checkbox的默认值 |
android:title="静音" />
<PreferenceScreen android:title="更多选项" >
唯一标记此组件 |
<CheckBoxPreference
android:defaultValue="true"
android:key="cb21"
checkbox关闭时的summary |
android:summaryOff="关闭"
android:summaryOn="开启"
组件的标题 |
android:title="功能1" />
<ListPreference
android:dialogTitle="请选择论坛"
android:entries="@array/entries_list_preference"
android:entryValues="@array/entriesvalue_list_preference"
android:key="list1"
android:summary="开发论坛"
android:title="android forum" />
<EditTextPreference
android:defaultValue="Hello EditTextPreference"
edittext组件的对话框标题 |
android:dialogTitle="输入设置"
android:key="et1"
android:summary="点击输入"
android:title="EditTextPreference Sample" />
</PreferenceScreen>
</PreferenceCategory>
<PreferenceCategory android:title="其他选项" >
<RingtonePreference
android:key="rt1"
android:summary="选择铃声"
android:title="RingtonePreference Sample" />
</PreferenceCategory>
</PreferenceScreen>
某些情况下, 你可能想要一个preference项来打开不同的activity而不是设置界面,就像一个web浏览器来查看一个web页面。当用户选择一个preference项时可以调用Intent来启动。方法就是添加一个<intent>节点到<Preference>节点中,示例代码如下:
<Preference android:title="@string/prefs_web_page" >
<intent android:action="android.intent.action.VIEW"
android:data="http://www.example.com" />
</Preference>
另外对listpreference的一些说明:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="entries_list_preference">
<item>www.anddev.org</item>
<item>www.eoeandroid.com</item>
<item>developer.android.com</item>
</string-array>
<string-array name="entriesvalue_list_preference">
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
</resources>
对于listpreference来说,里面的entries中给出的是给用户看的内容,entryValues是代码中需获取preference的值时的返回值。
对defaultValue,其实在用户第一次打开应用程序时看到的值,因此最好是为每一个preference对象指定一个默认值,然后再onCreate()方法中调用一次setDefaultValue()方法:
PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);
需要注意的是,这个boolean型的参数表示是否多次设置默认值,大部分情况下只需设置一次,因此此处是false。
可以看出,PreferenceActivity界面是由嵌套型的组件及preference的键值对组成,但是如何对用户的一系列操作进行响应呢?在之前学过的Activity中,常用布局里的监听,用findViewById()获得目标View,再在其上注册监听器即可,但是PreferenceActivity并没有提供findViewById()方法,也就无法监听。注意第一段代码中对key的解释:唯一标记此组件。利用组件的key值然后注册change监听器:
得到SharedPreferences句柄 |
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(this);
sp.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener() {
// sharedPreferences:句柄
注册改变监听器 |
// key: 改变键值
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
// TODO Auto-generated method stub
if (key.equals("autoBack")) {
Boolean autoBack = sharedPreferences.getBoolean("autoBack",
false);
if (autoBack) {
对应key值标记组件,并且得到value值 |
Log.d("TAG", "autoBack: true!");
} else {
Log.d("TAG", "autoBack: false!");
}
}
else if (key.equals("smsSilence")) {
//add action
}
}
});
手机设置是对手机全局的改变,通常情况下手机其他应用程序需要改变该数据,其他应用程序如何改变PreferenceActivity的值呢?
首先得到SharedPreferences实例
spc=this.getSharedPreferences("com.android.PreferenceActivity.Usage_setting.xml", MODE_WORLD_READABLE );
从spc中通过key获取对应的值
Boolean autoBack = spc.getBoolean("autoBack", false);
赋值:
public void putValue(String key, Boolean value){
spe = spc.edit();
spe.putBoolean(key, value);
spe.commit();
}
使用Preference Fragments
如果在android3.0或更高版本上开发,应该使用PreferenceFragment来显示Preference对象列表。而不应该在使用PreferenceActivity了。因为Fragments提供更为灵活的应用程序结构。
public static class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
...
}
然后把这个fragment添加到Activity中:
public class SettingsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
}
注意:一个PreferenceFragment没有它自己的Context对象。如果你需要一个Content对象,你能调用getActivity()方法。然而,当fragment没有附加到activity中或者activity声明周期结束时分离后,你使用getActivity()返回的将是null。
我们的代码中使用Preference Headers
在少数情况下,如用首次屏幕显示的时候,可能想要让用户先设置一些配置属性。在android3.0或更高版本系统下,可以使用新的“headers”功能来代替以前的subscreens的嵌套。使用headers步骤如下:
1. 每个单独的设置作为独立PreferenceFragment的实例。即,每组设置需要一个单独的XML文件。
2. 创建一个XML header文件,其中列出了每个设置组和声明这fragment包含相应的设置列表。
3. 继承PreferenceActivity类来托管设置。
4. 实现onBuildHeaders()回调方法用来指定头文件。
有意思的是,在大屏幕上使用Preference Headers这个设计运行时会自动给出双栏布局,如下图。
下面用一段代码来说明如何操作这个好用的Preference Headers。
<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header
每一个header声明一个PreferenceFragment实例,当用户选择这个header时就会打开这个PreferenceFragment |
android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
android:title="@string/prefs_category_one"
android:summary="@string/prefs_summ_category_one" />
<header
android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
android:title="@string/prefs_category_two"
android:summary="@string/prefs_summ_category_two" >
<extra android:name="someKey" android:value="someHeaderValue" />
这个键值对可以当成一个fragment的参数 |
</header>
</preference-headers>
对于<extra>的说明:
<extras>节点允许通过键值对的形式传参,一般是使用Bundle。Fragment通过调用getArguments()来得到参数。关于参数的用途比较常见的就是为每一个组重用相同的PreferenceFragment子类并且使用参数制定你将要载入哪一个preferences XML文件。例如,下面是一个fragment,它被多个设置组重用,下面代码清单5-11中在XML中使用了<extra>节点,key为“settings”:
public static class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String settings = getArguments().getString("settings");
if ("notifications".equals(settings)) {
addPreferencesFromResource(R.xml.settings_wifi);
} else if ("sync".equals(settings)) {
addPreferencesFromResource(R.xml.settings_sync);
}
}
}
为了实现headers,必须实现onBuildHeaders()回调方法并调用loadHeadersFromResource()方法,如下:
public class SettingsActivity extends PreferenceActivity {
@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
}
}