Android官方文档—User Interface(Settings)

本文介绍如何使用Android的Preference API构建应用程序设置,包括设置设计、在XML中定义偏好、创建偏好活动和片段、监听偏好变化以及构建自定义偏好。了解如何为Android应用创建一致的用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设置项

应用程序通常包含允许用户修改应用程序功能和行为的设置。例如,某些应用程序允许用户指定是启用通知还是指定应用程序与云同步数据的频率。

如果要为应用程序提供设置,则应使用Android的Preference API构建与其他Android应用程序(包括系统设置)中的用户体验一致的界面。本文档介绍如何使用Preference API构建应用程序设置。

设置设计

有关如何设置设置的信息,请阅读“设置”设计指南。

图1. Android Messaging应用程序设置的屏幕截图。选择“首选项”定义的项目将打开一个界面以更改设置。

概述


不是使用View对象来构建用户界面,而是使用您在XML文件中声明的Preference类的各种子类来构建设置。

Preference对象是单个设置的构建块。每个首选项都显示为列表中的项目,并为用户提供适当的UI以修改设置。例如,CheckBoxPreference创建一个显示复选框的列表项,ListPreference创建一个项目,打开一个包含选项列表的对话框。

您添加的每个首选项都有一个对应的键值对,系统使用该键值将设置保存在应用程序设置的默认SharedPreferences文件中。当用户更改设置时,系统会为您更新SharedPreferences文件中的相应值。您应该直接与关联的SharedPreferences文件进行交互的唯一时间是您需要读取该值以便根据用户的设置确定应用程序的行为。

SharedPreferences中为每个设置保存的值可以是以下数据类型之一:

  • Boolean
  • Float
  • Int
  • Long
  • String
  • String Set

因为您的应用程序的设置UI是使用Preference对象而不是View对象构建的,所以您需要使用专门的Activity或Fragment子类来显示列表设置:

  • 如果您的应用支持早于3.0的Android版本(API级别10及更低版本),则必须将活动构建为PreferenceActivity类的扩展。
  • 在Android 3.0及更高版本中,您应该使用传统的Activity来托管显示应用程序设置的PreferenceFragment。但是,如果有多组设置,还可以使用PreferenceActivity为大屏幕创建双窗格布局。

有关如何设置PreferenceActivity和PreferenceFragment的实例,请参阅有关创建首选项活动和使用首选项碎片的部分。

Preferences

应用程序的每个设置都由Preference类的特定子类表示。每个子类都包含一组核心属性,允许您指定诸如设置的标题和默认值之类的内容。每个子类还提供自己的专用属性和用户界面。例如,图1显示了Messaging应用程序设置的屏幕截图。设置屏幕中的每个列表项都由不同的Preference对象支持。

一些最常见的偏好是:

CheckBoxPreference

显示一个项目,其中包含启用或禁用设置的复选框。保存的值是布尔值(如果已选中,则为true)。

ListPreference

打开一个带有单选按钮列表的对话框。保存的值可以是任何一种受支持的值类型(如上所列)。

EditTextPreference

打开包含EditText小部件的对话框。保存的值是String。

请参阅Preference类以获取所有其他子类及其相应属性的列表。

当然,内置类不能满足所有需求,您的应用程序可能需要更专业的内容。例如,平台当前不提供用于选择数字或日期的Preference类。因此,您可能需要定义自己的Preference子类。有关此操作的帮助,请参阅有关构建自定义首选项的部分。

在XML中定义首选项


虽然您可以在运行时实例化新的Preference对象,但您应该使用Preference对象的层次结构在XML中定义设置列表。首选使用XML文件来定义设置集合,因为该文件提供了易于阅读的结构,易于更新。此外,您的应用程序的设置通常是预先确定的,但您仍然可以在运行时修改集合。

可以使用与类名匹配的XML元素声明每个Preference子类,例如<CheckBoxPreference>。

您必须将XML文件保存在res / xml /目录中。虽然您可以将文件命名为任何名称,但它通常命名为preferences.xml。您通常只需要一个文件,因为层次结构中的分支(打开它们自己的设置列表)是使用PreferenceScreen的嵌套实例声明的。

注意:如果要为设置创建多窗格布局,则每个片段需要单独的XML文件。

例如:

<?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。这两个项目包括以下三个属性:

android:key

对于保留数据值的首选项,此属性是必需的。它指定在SharedPreferences中保存此设置值时系统使用的唯一键(字符串)。

不需要此属性的唯一实例是首选项是PreferenceCategory或PreferenceScreen,或者首选项指定要调用的Intent(使用<intent>元素)或要显示的Fragment(使用android:fragment属性)。

android:title

这为设置提供了用户可见的名称。

android:defaultValue

这指定了系统应在SharedPreferences文件中设置的初始值。您应该为所有设置提供默认值。

图2.使用标题设置类别。
1.类别由<PreferenceCategory>元素指定。
2.使用android:title属性指定标题。

有关所有其他受支持属性的信息,请参阅首选项(和相应的子类)文档。

当您的设置列表超过大约10个项目时,您可能希望添加标题以定义设置组或在单独的屏幕中显示这些组。以下各节介绍了这些选项。

创建设置组

如果您显示10个或更多设置的列表,则用户可能难以扫描,理解和处理它们。您可以通过将部分或全部设置划分为组来解决此问题,从而有效地将一个长列表转换为多个较短列表。可以通过以下两种方式之一呈现一组相关设置:

  • Using titles
  • Using subscreens

您可以使用这些分组技术中的一种或两种来整理应用程序的设置。在决定使用哪种设置以及如何划分设置时,您应该遵循Android设计设置指南中的指南。

Using titles

如果要在设置组之间提供带有标题的分隔符(如图2所示),请将每组Preference对象放在PreferenceCategory中。

例如:

<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>

Using subscreens

Using subscreens

如果要将子组设置到子屏幕中(如图3所示),请将Preference对象组放在PreferenceScreen中。

图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>

使用意图

在某些情况下,您可能希望首选项打开不同的活动而不是设置屏幕,例如Web浏览器以查看网页。要在用户选择首选项时调用Intent,请将<intent>元素添加为相应<Preference>元素的子元素。

例如,以下是使用首选项打开网页的方法:

<Preference android:title="@string/prefs_web_page" >
    <intent android:action="android.intent.action.VIEW"
            android:data="http://www.example.com" />
</Preference>

您可以使用以下属性创建隐式和显式意图:

android:action

根据setAction()方法分配的操作。

android:data

要根据setData()方法分配的数据。

android:mimeType

要分配的MIME类型,根据setType()方法。

android:targetClass

组件名称的类部分,根据setComponent()方法。

android:targetPackage

组件名称的包部分,根据setComponent()方法。

创建首选项活动


要在活动中显示您的设置,请扩展PreferenceActivity类。这是传统Activity类的扩展,它根据Preference对象的层次结构显示设置列表。当用户进行更改时,PreferenceActivity会自动保留与每个首选项关联的设置。

注意:如果您正在为Android 3.0及更高版本开发应用程序,则应使用PreferenceFragment。转到有关使用首选项碎片的下一部分。

要记住的最重要的事情是在onCreate()回调期间不加载视图布局。相反,您调用addPreferencesFromResource()将您在XML文件中声明的首选项添加到活动中。例如,这是功能性PreferenceActivity所需的最低代码:

public class SettingsActivity extends PreferenceActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }
}

这实际上是某些应用程序的代码,因为只要用户修改了首选项,系统就会将更改保存到默认的SharedPreferences文件中,当您需要检查用户的设置时,其他应用程序组件可以读取该文件。但是,许多应用程序需要更多代码才能侦听首选项发生的更改。有关侦听SharedPreferences文件中的更改的信息,请参阅有关阅读首选项的部分。

使用首选项碎片


如果您正在开发Android 3.0(API级别11)及更高版本,则应使用PreferenceFragment显示Preference对象列表。您可以将PreferenceFragment添加到任何活动 - 您不需要使用PreferenceActivity。

与单独使用活动相比,碎片为您的应用程序提供了更灵活的体系结构,无论您正在构建什么样的活动。因此,我们建议您尽可能使用PreferenceFragment来控制设置的显示而不是PreferenceActivity。

您的PreferenceFragment实现可以像定义onCreate()方法一样简单,以使用addPreferencesFromResource()加载首选项文件。例如:

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);
    }
    ...
}

然后,您可以像处理任何其他片段一样将此片段添加到Activity中。例如:

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对象。如果需要Context对象,可以调用getActivity()。但是,只有在片段附加到活动时才要小心调用getActivity()。当片段尚未附加,或在其生命周期结束时被分离时,getActivity()将返回null。

设置默认值


您创建的首选项可能会为您的应用程序定义一些重要行为,因此在用户首次打开您的应用程序时,必须使用每个首选项的默认值初始化关联的SharedPreferences文件。

您必须做的第一件事是使用android:defaultValue属性为XML文件中的每个Preference对象指定一个默认值。该值可以是适用于相应Preference对象的任何数据类型。例如:

<!-- 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()期间调用此方法可确保使用默认设置正确初始化应用程序,应用程序可能需要读取该默认设置以确定某些行为(例如是否在蜂窝网络上下载数据)。

此方法有三个参数:

  • Context
  • 要为其设置默认值的首选项XML文件的资源ID。
  • 一个布尔值,指示是否应多次设置默认值。如果为false,则系统仅在以前从未调用此方法时设置默认值(或者默认值共享首选项文件中的KEY_HAS_SET_DEFAULT_VALUES为false)。

只要将第三个参数设置为false,就可以在每次活动开始时安全地调用此方法,而不会通过将其重置为默认值来覆盖用户保存的首选项。但是,如果将其设置为true,则将使用默认值覆盖任何先前的值。

使用首选项标题


在极少数情况下,您可能需要设计设置,使第一个屏幕仅显示子屏幕列表(例如在系统设置应用程序中,如图4和图5所示)。当您为Android 3.0及更高版本开发此类设计时,应使用“标题”功能,而不是使用嵌套的PreferenceScreen元素构建子屏幕。

要使用标头构建设置,您需要:

1.将每组字符串分隔为PreferenceFragment的单独实例。也就是说,每组设置都需要一个单独的XML文件。

2.创建一个列出每个设置组的XML头文件,并声明哪个片段包含相应的设置列表。

3.扩展PreferenceActivity类以托管您的设置。

4.实现onBuildHeaders()回调以指定头文件。

使用此设计的一大好处是PreferenceActivity在大屏幕上运行时会自动呈现图4所示的双窗格布局。

即使您的应用程序支持早于3.0的Android版本,您也可以构建应用程序以使用PreferenceFragment在较新设备上进行双窗格演示,同时仍支持旧设备上的传统多屏幕层次结构(请参阅有关支持旧版本的部分)首选项标题)。

图4.带有标题的双窗格布局。
1.标头使用XML头文件定义。
2.每组设置由PreferenceFragment定义,该PreferenceFragment由headers文件中的<header>元素指定。
图5.带有设置标题的手机设备。选择项目后,关联的PreferenceFragment将替换标题。

创建头文件

标头列表中的每组设置由根<preference-headers>元素内的单个<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:fragment属性,每个头都声明一个PreferenceFragment实例,该实例应在用户选择标题时打开。

<extras>元素允许您将键值对传递给Bundle中的片段。片段可以通过调用getArguments()来检索参数。您可能出于各种原因将参数传递给片段,但有一个很好的理由是为每个组重用相同的PreferenceFragment子类,并使用该参数指定片段应加载哪些首选项XML文件。

例如,当每个标头使用“settings”键定义<extra>参数时,这是一个可以重复用于多个设置组的片段:

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);
        }
    }
}

显示标题

要显示首选项标头,必须实现onBuildHeaders()回调方法并调用loadHeadersFromResource()。例如:

public class SettingsActivity extends PreferenceActivity {
    @Override
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.preference_headers, target);
    }
}

当用户从标题列表中选择一个项目时,系统将打开关联的PreferenceFragment。

注意:使用首选项标头时,PreferenceActivity的子类不需要实现onCreate()方法,因为活动唯一需要的任务是加载标头。

支持带有首选项标头的旧版本

如果您的应用程序支持早于3.0的Android版本,则在Android 3.0及更高版本上运行时,您仍然可以使用标题提供双窗格布局。您需要做的就是创建一个额外的首选项XML文件,该文件使用基本<Preference>元素,其行为类似于标题项(将由较旧的Android版本使用)。

但是,不是打开新的PreferenceScreen,而是每个<Preference>元素都会向PreferenceActivity发送一个Intent,指定要加载的首选XML文件。

例如,这是在Android 3.0及更高版本(res / xml / preference_headers.xml)上使用的首选项标头的XML文件:

<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>

这是一个首选项文件,为早于Android 3.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中添加了对<preference-headers>的支持,因此只有在Android 3.0或更高版本上运行时,系统才会调用PreferenceActivity中的onBuildHeaders()。要加载“遗留”头文件(preference_headers_legacy.xml),您必须检查Android版本,如果版本早于Android 3.0(HONEYCOMB),请调用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);
}

剩下要做的唯一事情就是处理传递给活动的Intent,以确定要加载的首选项文件。因此,检索intent的操作并将其与您在首选项XML的<intent>标记中使用的已知操作字符串进行比较:

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()的连续调用会将所有首选项堆叠在一个列表中,因此请确保仅通过使用else-if语句链接条件来调用它。

Reading Preferences


默认情况下,通过调用静态方法PreferenceManager.getDefaultSharedPreferences(),可以将所有应用程序的首选项保存到可从应用程序中的任何位置访问的文件中。这将返回SharedPreferences对象,该对象包含与PreferenceActivity中使用的Preference对象关联的所有键值对。

例如,以下是您可以从应用程序中的任何其他活动中读取其中一个首选项值的方法:

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");

监听偏好变化

一旦用户更改其中一个首选项,您可能希望收到通知的原因有多种。为了在任何一个首选项发生更改时接收回调,请实现SharedPreference.OnSharedPreferenceChangeListener接口,并通过调用registerOnSharedPreferenceChangeListener()注册SharedPreferences对象的侦听器。

该接口只有一个回调方法onSharedPreferenceChanged(),您可能会发现最简单的方法是将接口作为活动的一部分来实现。例如:

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()来获取已更改的Preference对象,以便它可以修改项目的摘要以作为用户选择的描述。也就是说,当设置是ListPreference或其他多项选择设置时,您应该在设置更改时调用setSummary()以显示当前状态(例如图5中所示的Sleep设置)。

注意:如关于设置的Android设计文档中所述,我们建议您每次更改首选项时更新ListPreference的摘要以描述当前设置。

为了在活动中进行适当的生命周期管理,我们建议您分别在onResume()和onPause()回调期间注册和注销SharedPreferences.OnSharedPreferenceChangeListener:

@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添加一个intent过滤器。例如:

<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>

此intent过滤器向系统指示这是控制应用程序数据使用情况的活动。因此,当用户从系统的“设置”应用程序检查您的应用程序使用了多少数据时,可以使用“查看应用程序设置”按钮启动PreferenceActivity,以便用户可以优化应用程序使用的数据量。

构建自定义首选项


Android框架包含各种Preference子类,允许您为几种不同类型的设置构建UI。但是,您可能会发现没有内置解决方案所需的设置,例如数字选择器或日期选择器。在这种情况下,您需要通过扩展Preference类或其他子类之一来创建自定义首选项。

扩展Preference类时,您需要做一些重要的事情:

  • 指定用户选择设置时显示的用户界面。
  • 适当时保存设置的值。
  • 在进入视图时,使用当前(或默认)值初始化首选项。
  • 在系统请求时提供默认值。
  •  如果Preference提供了自己的UI(例如对话框),则保存并恢复状态以处理生命周期更改(例如用户旋转屏幕时)。

以下部分描述了如何完成这些任务。

指定用户界面

如果直接扩展Preference类,则需要实现onClick()以定义用户选择项时发生的操作。但是,大多数自定义设置都会扩展DialogPreference以显示对话框,从而简化了过程。扩展DialogPreference时,必须在类构造函数中调用setDialogLayoutResourcs()以指定对话框的布局。

例如,这是自定义DialogPreference的构造函数,它声明了布局并指定了默认正面和负面对话框按钮的文本:

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);
    }
    ...
}

保存设置的值

您可以通过调用Preference类的persist *()方法之一随时保存设置的值,例如persistInt()如果设置的值是整数,或者persistBoolean()来保存布尔值。

注意:每个首选项只能保存一种数据类型,因此必须使用适用于自定义首选项使用的数据类型的persist *()方法。

当您选择保留时,设置可能取决于您扩展的Preference类。如果扩展DialogPreference,则只有在对话框关闭时才会保留该值,因为结果为正(用户选择“确定”按钮)。

DialogPreference关闭时,系统调用onDialogClosed()方法。该方法包括一个布尔参数,用于指定用户结果是否为“正” - 如果值为true,则用户选择肯定按钮,您应该保存新值。例如:

@Override
protected void onDialogClosed(boolean positiveResult) {
    // When the user selects "OK", persist the new value
    if (positiveResult) {
        persistInt(mNewValue);
    }
}

在此示例中,mNewValue是一个保存设置当前值的类成员。调用persistInt()将值保存到SharedPreferences文件(使用在此Preference的XML文件中指定的密钥自动)。

初始化当前值

当系统将您的首选项添加到屏幕时,它会调用onSetInitialValue()来通知您该设置是否具有持久值。如果没有持久值,则此调用将为您提供默认值。

onSetInitialValue()方法传递一个boolean,restorePersistedValue,以指示是否已为该设置保留一个值。如果是,那么您应该通过调用Preference类的getPersisted *()方法之一来检索持久值,例如getPersistedInt()为整数值。您通常希望检索持久值,以便正确更新UI以反映以前保存的值。

如果restorePersistedValue为false,则应使用在第二个参数中传递的默认值。

@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()无法返回持久值,则使用局部常量来指定默认值。

警告:您不能将defaultValue用作getPersisted *()方法中的默认值,因为当restorePersistedValue为true时,其值始终为null。

提供默认值

如果Preference类的实例指定了默认值(使用android:defaultValue属性),则系统在实例化对象以便检索值时调用onGetDefaultValue()。您必须实现此方法,以便系统将默认值保存在SharedPreferences中。例如:

@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
    return a.getInteger(index, DEFAULT_VALUE);
}

方法参数提供了您需要的一切:属性数组和android:defaultValue的索引位置,您必须检索它们。您必须实现此方法以从属性中提取默认值的原因是,如果值未定义,则必须为该属性指定本地默认值。

保存和恢复首选项的状态

就像布局中的View一样,Preference子类负责在重新启动活动或片段时(例如当用户旋转屏幕时)保存和恢复其状态。要正确保存和恢复Preference类的状态,必须在onSaveInstanceState()和onRestoreInstanceState()上实现生命周期回调方法。

Preference的状态由实现Parcelable接口的对象定义。 Android框架为您提供了一个定义状态对象的起点:Preference.BaseSavedState类。

要定义Preference类如何保存其状态,应扩展Preference.BaseSavedState类。您只需要覆盖几个方法并定义CREATOR对象。

对于大多数应用程序,如果Preference子类保存的数据类型不是整数,则可以复制以下实现并简单地更改处理该值的行。

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的上述实现添加到您的应用程序(通常作为Preference子类的子类),您需要为Preference子类实现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);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值