Settings
应用程序通常包括设置,允许用户修改应用程序的功能和行为。例如,一些应用程序允许用户指定通知是否启用或指定应用程序的频率同步数据与云。
如果您想为您的应用程序的设置,你应该使用Android的偏好API来构建与其他Android应用(包括系统设置),用户的体验是一致的接口。本文介绍了如何建立使用偏好的API应用设置。
设置设计
有关如何设计你设置的信息,请阅读设置设计指南。
图1.从Android短信应用的设置截图。选择由偏好定义的项目打开来更改设置的接口。
概观
而不是使用视图对象构建用户界面,设置使用您在XML文件中声明优先级的不同子类中建。
A优先股对象是单个设置积木。每个偏好显示为列表中的一个项目,并为用户提供了修改设置适当的UI。例如,一个CheckBoxPreference创建一个列表项,显示一个复选框,一个ListPreference创建打开与一个选择列表的对话框的一个项目。
每次添加偏好具有系统用来保存设置为您的应用程序的设置默认SharedPreferences文件中的相应键值对。当用户更改设置,系统更新为你SharedPreferences文件的相应值。你应该直接与相关的SharedPreferences文件交互的唯一时间是当你需要以确定根据用户设置你的应用程序的行为来读取值。
保存在SharedPreferences每个设置可以是以下数据类型之一的值:
Set
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <CheckBoxPreference android:key="pref_sync" android:title="@string/pref_sync" android:summary="@string/pref_sync_summ" android:defaultValue="true" /> <ListPreference android:dependency="pref_sync" android:key="pref_syncConnectionType" android:title="@string/pref_syncConnectionType" android:dialogTitle="@string/pref_syncConnectionType" android:entries="@array/pref_syncConnectionTypes_entries" android:entryValues="@array/pref_syncConnectionTypes_values" android:defaultValue="@string/pref_syncConnectionTypes_default" /> </PreferenceScreen>在这个例子中,有一个CheckBoxPreference和ListPreference。两个项目包括以下三个属性:
图2.标题设置的类别。
1.类别由<PreferenceCategory>元素中指定。
title属性:2,标题与Android指定。
当您的设置列表超过约10个项目,你可能要添加标题定义的设置组或单独的屏幕上显示这些组。这些选项在下面的章节中描述。
创建设置组
如果你目前的10个或更多的设置列表中,用户可能难以扫描,理解和处理它们。可以通过将一些或全部的设置成组,有效地将一个长列表分为多个较短的列表解决这个问题。一组相关的设置,可以通过以下两种方式之一提交:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceCategory android:title="@string/pref_sms_storage_title" android:key="pref_key_storage_settings"> <CheckBoxPreference android:key="pref_key_auto_delete" android:summary="@string/pref_summary_auto_delete" android:title="@string/pref_title_auto_delete" android:defaultValue="false"... /> <Preference android:key="pref_key_sms_delete_limit" android:dependency="pref_key_auto_delete" android:summary="@string/pref_summary_delete_limit" android:title="@string/pref_title_sms_delete"... /> <Preference android:key="pref_key_mms_delete_limit" android:dependency="pref_key_auto_delete" android:summary="@string/pref_summary_delete_limit" android:title="@string/pref_title_mms_delete" ... /> </PreferenceCategory> ... </PreferenceScreen>使用 subscreens
图3.设置防晒霜。在<PreferenceScreen>元素创建,选中时,打开一个单独的列表中显示嵌套设置的项目。
例如:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <!-- opens a subscreen of settings --> <PreferenceScreen android:key="button_voicemail_category_key" android:title="@string/voicemail" android:persistent="false"> <ListPreference android:key="button_voicemail_provider_key" android:title="@string/voicemail_provider" ... /> <!-- opens another nested subscreen --> <PreferenceScreen android:key="button_voicemail_setting_key" android:title="@string/voicemail_settings" android:persistent="false"> ... </PreferenceScreen> <RingtonePreference android:key="button_voicemail_ringtone_key" android:title="@string/voicemail_ringtone_title" android:ringtoneType="notification" ... /> ... </PreferenceScreen> ... </PreferenceScreen>使用意图
<Preference android:title="@string/prefs_web_page" > <intent android:action="android.intent.action.VIEW" android:data="http://www.example.com" /> </Preference>您可以通过以下属性创建的隐式和显式意图:
public class SettingsActivity extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); } }这实际上是对某些应用足够的代码,因为只要用户修改的偏好,系统将更改保存到默认SharedPreferences文件,当你需要检查用户的设置的其他应用程序组件可以读取。许多应用程序,但是,需要以多一点的代码来监听所发生的偏好的变化。有关听着SharedPreferences文件更改的信息,请参阅有关阅读喜好的部分。
public static class SettingsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Load the preferences from an XML resource addPreferencesFromResource(R.xml.preferences); } ... }然后,您可以将此片段添加到活动就像你其他任何片段。 例如:
public class SettingsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Display the fragment as the main content. getFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()) .commit(); } }注:PreferenceFragment没有一个自己的上下文对象。如果你需要一个Context对象,你可以调用getActivity()。但是,要小心调用getActivity()只有当片段连接到一个活动。当该片段尚未连接,或者其生命周期,getActivity年底期间分离()将返回null。
<!-- default value is a boolean --> <CheckBoxPreference android:defaultValue="true" ... /> <!-- default value is a string --> <ListPreference android:defaultValue="@string/pref_syncConnectionTypes_default" ... />然后,从您的应用程序的主的onCreate()方法的活动,并通过它用户可以输入首次呼叫setDefaultValues)您的应用程序(任何其他活动:
PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);的onCreate()中调用这可以确保您的应用程序正常初始化使用默认设置,您的应用程序可能需要以确定某些行为(如是否下载,而蜂窝网络上的数据)来读取。
图4.头两窗格布局。
1.头与一个XML头文件中定义。
2.设置每个组是由是由在头文件中的<header>元素指定的PreferenceFragment定义。
图5.设置标题的手持设备。当选择了一个项目,相关PreferenceFragment更换头。
创建头文件
在你的头列表设置每个组由一个根<偏好头>元素中一个<header>元素指定。 例如:
<?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="@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" > <!-- key/value pairs can be included as arguments for the fragment. --> <extra android:name="someKey" android:value="someHeaderValue" /> </header> </preference-headers>随着Android:片段属性,每一个标题声明PreferenceFragment的一个实例,当用户选择的标题应该打开。
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); } } }显示标题
public class SettingsActivity extends PreferenceActivity { @Override public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.preference_headers, target); } }当用户选择从标题的列表中的项目,系统将打开相关PreferenceFragment。
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <header android:fragment="com.example.prefs.SettingsFragmentOne" android:title="@string/prefs_category_one" android:summary="@string/prefs_summ_category_one" /> <header android:fragment="com.example.prefs.SettingsFragmentTwo" android:title="@string/prefs_category_two" android:summary="@string/prefs_summ_category_two" /> </preference-headers>这里是它提供了比Android3.0(RES / XML / preference_headers_legacy.xml)老版本相同的头一个首选项文件:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <Preference android:title="@string/prefs_category_one" android:summary="@string/prefs_summ_category_one" > <intent android:targetPackage="com.example.prefs" android:targetClass="com.example.prefs.SettingsActivity" android:action="com.example.prefs.PREFS_ONE" /> </Preference> <Preference android:title="@string/prefs_category_two" android:summary="@string/prefs_summ_category_two" > <intent android:targetPackage="com.example.prefs" android:targetClass="com.example.prefs.SettingsActivity" android:action="com.example.prefs.PREFS_TWO" /> </Preference> </PreferenceScreen>因为对于<偏好头>支持Android 3.0的加入,系统上Androd3.0或更高版本上运行,只有当调用onBuildHeaders()在你的PreferenceActivity。为了加载了“遗产”头文件(偏好头legacy.xml),必须检查Android版本,如果版本低于3.0的Android(蜂窝)年纪大了,叫addPreferencesFromResource()来加载传统的头文件。 例如:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { // Load the legacy preferences headers addPreferencesFromResource(R.xml.preference_headers_legacy); } } // Called only on Honeycomb and later @Override public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.preference_headers, target); }剩下要做的唯一事情就是搞定的传入活动,以确定要加载的首选项文件的意图。所以检索的意图行动,并把它比作是您在偏好XML <目的>标签中使用已知的动作字符串:
final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE"; ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String action = getIntent().getAction(); if (action != null && action.equals(ACTION_PREFS_ONE)) { addPreferencesFromResource(R.xml.preferences); } ... else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { // Load the legacy preferences headers addPreferencesFromResource(R.xml.preference_headers_legacy); } }谨防)的连续调用addPreferencesFromResource(会叠加在一个列表中的所有喜好,所以要确保它只能通过链接的条件调用一次,否则-if语句。
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");监听偏好的变化
public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener { public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType"; ... public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(KEY_PREF_SYNC_CONN)) { Preference connectionPref = findPreference(key); // Set summary to be the user-description for the selected value connectionPref.setSummary(sharedPreferences.getString(key, "")); } } }在这个例子中,该方法检查所改变的设置是否为已知的偏爱项。它调用findPreference()来获取已更改,以便它可以修改的项目的总结是用户选择的描述偏好的对象。也就是说,当设置为一个ListPreference或其他选择题的设置,你应该叫setSummary()时设置更改为显示(例如图5所示的睡眠设置)的当前状态。
@Override protected void onResume() { super.onResume(); getPreferenceScreen().getSharedPreferences() .registerOnSharedPreferenceChangeListener(this); } @Override protected void onPause() { super.onPause(); getPreferenceScreen().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener(this); }注意:当你调用registerOnSharedPreferenceChangeListener(),偏好管理器不会保存当前强烈的参考监听器。您必须存储的强引用监听器,否则会容易受到垃圾收集。我们建议您保持,只要您需要的侦听器将存在一个对象的实例数据到听者的参考。
prefs.registerOnSharedPreferenceChangeListener( // Bad! The listener is subject to garbage collection! new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // listener implementation } });相反,存储在只要需要监听,将存在一个对象的一个实例的数据字段中的监听器的参考:
SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // listener implementation } }; prefs.registerOnSharedPreferenceChangeListener(listener);管理网络使用
与Android 4.0开始,系统的设置应用程序允许用户看到他们的应用程序的网络有多少数据在前台和后台使用一段时间。然后,用户可以禁止使用单个应用程序的背景资料。为了避免用户禁用您的应用从后台数据的访问,那么应该有效地利用数据连接,并允许用户通过应用程序的设置优化您的应用程序的数据使用量。
例如,可以允许用户控制您的应用程序的频率同步的数据,你的应用程序是否执行上传/只时,通过Wi-Fi下载,您的应用程序是否使用数据漫游时,等有了提供给他们这些控件,用户不太可能,当他们接近,他们在系统设置中设置的限制禁用您的应用程序的访问数据,因为它们可以代替精确地控制您的应用程序有多少数据使用。
一旦你在你的PreferenceActivity控制应用数据的习惯添加了必要的喜好,你应该在你的清单文件中添加了一个ACTION_MANAGE_NETWORK_USAGE意图过滤器。例如
<activity android:name="SettingsActivity" ... > <intent-filter> <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>这个意图过滤指示系统,这是一种控制应用程序的数据使用量的活动。因此,当用户检查多少数据您的应用程序从系统的设置应用程序使用,查看一个应用程序设置按钮是可用的启动您的PreferenceActivity,使用户可以优化您的应用程序有多少数据使用。
public class NumberPickerPreference extends DialogPreference { public NumberPickerPreference(Context context, AttributeSet attrs) { super(context, attrs); setDialogLayoutResource(R.layout.numberpicker_dialog); setPositiveButtonText(android.R.string.ok); setNegativeButtonText(android.R.string.cancel); setDialogIcon(null); } ... }保存设置的值
@Override protected void onDialogClosed(boolean positiveResult) { // When the user selects "OK", persist the new value if (positiveResult) { persistInt(mNewValue); } }在这个例子中,mNewValue是一个类的成员持有的设定的当前值。调用persistInt()保存价值的SharedPreferences文件(自动使用的XML文件中指定此偏好的关键)。
@Override protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { if (restorePersistedValue) { // Restore existing state mCurrentValue = this.getPersistedInt(DEFAULT_VALUE); } else { // Set default state from the XML attribute mCurrentValue = (Integer) defaultValue; persistInt(mCurrentValue); } }每个getPersisted*()方法采用指定情况下使用默认值实际上是没有持久的价值或该键不存在的说法。在上面的例子中,一个本地常数用于指定在壳体getPersistedInt()的默认值不能返回一个持久的值。
@Override protected Object onGetDefaultValue(TypedArray a, int index) { return a.getInteger(index, DEFAULT_VALUE); }该方法的参数提供你需要的一切:属性的阵列和android的索引位置:设置defaultValue,你必须检索。您必须实现此方法提取属性的默认值的原因是因为你必须指定的情况下,该值是不确定的属性的本地默认值。
private static class SavedState extends BaseSavedState { // Member that holds the setting's value // Change this data type to match the type saved by your Preference int value; public SavedState(Parcelable superState) { super(superState); } public SavedState(Parcel source) { super(source); // Get the current preference's value value = source.readInt(); // Change this to read the appropriate data type } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); // Write the preference's value dest.writeInt(value); // Change this to write the appropriate data type } // Standard creator object using an instance of this class public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; }与上述实施Preference.BaseSavedState的添加到您的应用程序(通常是你的偏好的子类的子类),你需要实现的onSaveInstanceState的()和onRestoreInstanceState()方法,为您偏好的子类。
@Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); // Check whether this Preference is persistent (continually saved) if (isPersistent()) { // No need to save instance state since it's persistent, // use superclass state return superState; } // Create instance of custom BaseSavedState final SavedState myState = new SavedState(superState); // Set the state's value with the class member that holds current // setting value myState.value = mNewValue; return myState; } @Override protected void onRestoreInstanceState(Parcelable state) { // Check whether we saved the state in onSaveInstanceState if (state == null || !state.getClass().equals(SavedState.class)) { // Didn't save the state, so call superclass super.onRestoreInstanceState(state); return; } // Cast state to custom BaseSavedState and pass to superclass SavedState myState = (SavedState) state; super.onRestoreInstanceState(myState.getSuperState()); // Set this Preference's widget to reflect the restored state mNumberPicker.setValue(myState.value); }