1page.title=设置
2page.tags=首选项,首选项 Activity,首选项片段
3
4@jd:body
5
6
7<div id="qv-wrapper">
8<div id="qv">
9
10<h2>本文内容</h2>
11<ol>
12  <li><a href="#Overview">概览</a>
13    <ol>
14      <li><a href="#SettingTypes">首选项</a></li>
15    </ol>
16  </li>
17  <li><a href="#DefiningPrefs">使用 XML 定义首选项</a>
18    <ol>
19      <li><a href="#Groups">创建设置组</a></li>
20      <li><a href="#Intents">使用 Intent </a></li>
21    </ol>
22  </li>
23  <li><a href="#Activity">创建首选项 Activity</a></li>
24  <li><a href="#Fragment">使用首选项片段</a></li>
25  <li><a href="#Defaults">设置默认值</a></li>
26  <li><a href="#PreferenceHeaders">使用首选项标头</a>
27    <ol>
28      <li><a href="#CreateHeaders">创建标头文件</a></li>
29      <li><a href="#DisplayHeaders">显示标头</a></li>
30      <li><a href="#BackCompatHeaders">使用首选项标头支持旧版本</a></li>
31    </ol>
32  </li>
33  <li><a href="#ReadingPrefs">读取首选项</a>
34    <ol>
35      <li><a href="#Listening">侦听首选项变更</a></li>
36    </ol>
37  </li>
38  <li><a href="#NetworkUsage">管理网络使用情况</a></li>
39  <li><a href="#Custom">构建自定义首选项</a>
40    <ol>
41      <li><a href="#CustomSelected">指定用户界面</a></li>
42      <li><a href="#CustomSave">保存设置的值</a></li>
43      <li><a href="#CustomInitialize">初始化当前值</a></li>
44      <li><a href="#CustomDefault">提供默认值</a></li>
45      <li><a href="#CustomSaveState">保存和恢复首选项的状态</a></li>
46    </ol>
47  </li>
48</ol>
49
50<h2>关键类</h2>
51<ol>
52  <li>{@link android.preference.Preference}</li>
53  <li>{@link android.preference.PreferenceActivity}</li>
54  <li>{@link android.preference.PreferenceFragment}</li>
55</ol>
56
57
58<h2>另请参阅</h2>
59<ol>
60  <li><a href="{@docRoot}design/patterns/settings.html">设置设计指南</a></li>
61</ol>
62</div>
63</div>
64
65
66
67
68<p>应用通常包括允许用户修改应用特性和行为的设置。例如,有些应用允许用户指定是否启用通知,或指定应用与云端同步数据的频率。
69
70</p>
71
72<p>若要为应用提供设置,您应该使用
73Android 的 {@link android.preference.Preference} API
74构建一个与其他 Android 应用中的用户体验一致的界面(包括系统设置)。本文旨在介绍如何使用
75{@link android.preference.Preference} API 构建应用设置。</p>
76
77<div class="note design">
78<p><strong>设置设计</strong></p>
79  <p>有关如何设计设置的信息,请阅读<a href="{@docRoot}design/patterns/settings.html">设置</a>设计指南。</p>
80</div>
81
82
83<img src="{@docRoot}images/ui/settings/settings.png" alt="" width="435" />
84<p class="img-caption"><strong>图 1. </strong>来自 Android 信息应用的设置的屏幕截图。
85选择由 {@link android.preference.Preference}
86定义的项目将打开一个用于更改设置的界面。</p>
87
88
89
90
91<h2 id="Overview">概览</h2>
92
93<p>设置是使用您在 XML 文件中声明的
94{@link android.preference.Preference}
95类的各种子类构建而成,而不是使用 {@link android.view.View} 对象构建用户界面。</p>
96
97<p>{@link android.preference.Preference}
98对象是单个设置的构建基块。每个 {@link android.preference.Preference} 均作为项目显示在列表中,并提供适当的
99UI 供用户修改设置。例如,{@link
100android.preference.CheckBoxPreference} 可创建一个列表项用于显示复选框,{@link
101android.preference.ListPreference} 可创建一个项目用于打开包含选择列表的对话框。</p>
102
103<p>您添加的每个 {@link android.preference.Preference} 都有一个相应的键值对,可供系统用来将设置保存在应用设置的默认
104{@link android.content.SharedPreferences}
105文件中。当用户更改设置时,系统会为您更新
106{@link android.content.SharedPreferences} 文件中的相应值。您只应在需要读取值以根据用户设置确定应用的行为时,才与关联的
107{@link android.content.SharedPreferences}
108文件直接交互。</p>
109
110<p>为每个设置保存在 {@link android.content.SharedPreferences}
111中的值可能是以下数据类型之一:</p>
112
113<ul>
114  <li>布尔型</li>
115  <li>浮点型</li>
116  <li>整型</li>
117  <li>长整型</li>
118  <li>字符串</li>
119  <li>字符串 {@link java.util.Set}</li>
120</ul>
121
122<p>由于应用的设置 UI 是使用 {@link android.preference.Preference}
123对象(而非
124{@link android.view.View} 对象)构建而成,因此您需要使用专门的 {@link android.app.Activity} 或
125{@link android.app.Fragment} 子类显示列表设置:</p>
126
127<ul>
128  <li>如果应用支持早于 3.0(API 级别 10 及更低级别)的 Android 版本,则您必须将 Activity 构建为
129{@link android.preference.PreferenceActivity} 类的扩展。</li>
130  <li>对于 Android 3.0 及更高版本,您应改用传统
131{@link android.app.Activity},以托管可显示应用设置的 {@link android.preference.PreferenceFragment}。但是,如果您拥有多组设置,则还可以使用
132{@link android.preference.PreferenceActivity}
133为大屏幕创建双窗格布局。</li>
134</ul>
135
136<p><a href="#Activity">创建首选项 Activity</a>和<a href="#Fragment">使用首选项片段</a>
137部分将讨论如何设置 {@link android.preference.PreferenceActivity} 以及 {@link
138android.preference.PreferenceFragment} 实例。</p>
139
140
141<h3 id="SettingTypes">首选项</h3>
142
143<p>所有应用设置均由 {@link
144android.preference.Preference} 类的特定子类表示。每个子类均包括一组核心属性,允许您指定设置标题和默认值等内容。
145此外,每个子类还提供自己的专用属性和用户界面。
146例如,图 1 显示的是“信息”
147应用的设置屏幕截图。设置屏幕中的每个列表项均由不同的 {@link
148android.preference.Preference} 对象提供支持。</p>
149
150<p>一些最常用的首选项如下:</p>
151
152<dl>
153  <dt>{@link android.preference.CheckBoxPreference}</dt>
154  <dd>显示一个包含已启用或已禁用设置复选框的项目。保存的值是布尔型(如果选中则为
155<code>true</code>)。</dd>
156
157  <dt>{@link android.preference.ListPreference}</dt>
158  <dd>打开一个包含单选按钮列表的对话框。保存的值可以是任一受支持的值类型(如上所列)。
159</dd>
160
161  <dt>{@link android.preference.EditTextPreference}</dt>
162  <dd>打开一个包含 {@link android.widget.EditText} 小工具的对话框。保存的值是 {@link
163java.lang.String}。</dd>
164</dl>
165
166<p>有关所有其他子类及其对应属性的列表,请参阅 {@link android.preference.Preference}
167类。</p>
168
169<p>当然,内置类不能满足所有需求,您的应用可能需要更专业化的内容。
170例如,该平台目前不提供用于选取数字或日期的 {@link
171android.preference.Preference} 类。因此,您可能需要定义自己的
172{@link android.preference.Preference} 子类。如需有关执行此操作的帮助,请参阅<a href="#Custom">构建自定义首选项</a>部分。</p>
173
174
175
176<h2 id="DefiningPrefs">使用 XML 定义首选项</h2>
177
178<p>虽然您可以在运行时实例化新的 {@link android.preference.Preference}
179对象,不过您还是应该使用 {@link android.preference.Preference}
180对象的层次结构在 XML 中定义设置列表。使用 XML
181文件定义设置的集合是首选方法,因为该文件提供了一个便于更新的易读结构。此外,应用的设置通常是预先确定的,不过您仍可在运行时修改此集合。
182</p>
183
184<p>每个 {@link android.preference.Preference} 子类均可以使用与类名(如 {@code &lt;CheckBoxPreference&gt;})匹配的 XML 元素来声明。
185</p>
186
187<p>您必须将 XML 文件保存在 {@code res/xml/} 目录中。尽管您可以随意命名该文件,但它通常命名为
188{@code preferences.xml}。您通常只需一个文件,因为层次结构中的分支(可打开各自的设置列表)是使用
189{@link android.preference.PreferenceScreen}
190的嵌套实例声明的。</p>
191
192<p class="note"><strong>注:</strong>若要为设置创建多窗格布局,则需要为每个片段提供单独的
193XML 文件。</p>
194
195<p>XML 文件的根节点必须是一个 {@link android.preference.PreferenceScreen
196&lt;PreferenceScreen&gt;} 元素。您可以在此元素内添加每个 {@link
197android.preference.Preference}。在
198{@link android.preference.PreferenceScreen &lt;PreferenceScreen&gt;}
199元素内添加的每个子项均将作为单独的项目显示在设置列表中。</p>
200
201<p>例如:</p>
202
203<pre>
204&lt;?xml version="1.0" encoding="utf-8"?>
205&lt;PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
206    &lt;CheckBoxPreference
207        android:key="pref_sync"
208        android:title="@string/pref_sync"
209        android:summary="@string/pref_sync_summ"
210        android:defaultValue="true" />
211    &lt;ListPreference
212        android:dependency="pref_sync"
213        android:key="pref_syncConnectionType"
214        android:title="@string/pref_syncConnectionType"
215        android:dialogTitle="@string/pref_syncConnectionType"
216        android:entries="@array/pref_syncConnectionTypes_entries"
217        android:entryValues="@array/pref_syncConnectionTypes_values"
218        android:defaultValue="@string/pref_syncConnectionTypes_default" />
219&lt;/PreferenceScreen>
220</pre>
221
222<p>在此示例中,有一个 {@link android.preference.CheckBoxPreference} 和 {@link
223android.preference.ListPreference}。这两项均包括以下三个属性:</p>
224
225<dl>
226  <dt>{@code android:key}</dt>
227  <dd>对于要保留数据值的首选项,必须拥有此属性。它指定系统在将此设置的值保存在
228{@link
229android.content.SharedPreferences} 中时所用的唯一键(字符串)。
230  <p>不需要此属性的仅有情形是:首选项是
231{@link android.preference.PreferenceCategory} 或{@link android.preference.PreferenceScreen},或者首选项指定要调用的
232{@link android.content.Intent}(使用 <a href="#Intents">{@code &lt;intent&gt;}</a> 元素)或要显示的 {@link android.app.Fragment}(使用 <a href="{@docRoot}reference/android/preference/Preference.html#attr_android:fragment">{@code
233android:fragment}</a> 属性)。<em></em></p>
234  </dd>
235  <dt>{@code android:title}</dt>
236  <dd>此属性为设置提供用户可见的名称。</dd>
237  <dt>{@code android:defaultValue}</dt>
238  <dd>此属性指定系统应该在 {@link
239android.content.SharedPreferences} 文件中设置的初始值。您应该为所有设置提供默认值。
240</dd>
241</dl>
242
243<p>有关所有其他受支持属性的信息,请参阅 {@link
244android.preference.Preference}(和相应子类)文档。</p>
245
246
247<div class="figure" style="width:300px">
248  <img src="{@docRoot}images/ui/settings/settings-titles.png" alt="" />
249  <p class="img-caption"><strong>图 2. </strong>带标题的设置类别。
250 <br/><b>1.</b>类别由 {@link
251android.preference.PreferenceCategory &lt;PreferenceCategory&gt;} 元素指定。 <br/><b>2.</b>标题由
252{@code android:title} 属性指定。</p>
253</div>
254
255
256<p>当设置列表超过 10 项时,您可能需要添加标题来定义设置组或在单独的屏幕中显示这些组。
257
258下文将介绍这些选项。</p>
259
260
261<h3 id="Groups">创建设置组</h3>
262
263<p>如果您提供的列表包含 10
264项或更多设置,则用户可能难以浏览、理解和处理这些设置。若要弥补这一点,您可以将部分或全部设置分成若干组,从而有效地将一个长列表转化为多个短列表。
265
266可以通过下列两种方法之一提供一组相关设置:</p>
267
268<ul>
269  <li><a href="#Titles">使用标题</a></li>
270  <li><a href="#Subscreens">使用子屏幕</a></li>
271</ul>
272
273<p>您可以使用其中一种或两种分组方法来组织应用的设置。决定要使用的技巧以及如何拆分设置时,应遵循 Android 设计的<a href="{@docRoot}design/patterns/settings.html">设置</a>指南中的准则。
274
275</p>
276
277
278<h4 id="Titles">使用标题</h4>
279
280<p>若要以分隔线分隔两组设置并为其提供标题(如图 2 所示),请将每组
281{@link android.preference.Preference} 对象放入 {@link
282android.preference.PreferenceCategory} 内。</p>
283
284<p>例如:</p>
285
286<pre>
287&lt;PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
288    &lt;PreferenceCategory
289        android:title="&#64;string/pref_sms_storage_title"
290        android:key="pref_key_storage_settings">
291        &lt;CheckBoxPreference
292            android:key="pref_key_auto_delete"
293            android:summary="&#64;string/pref_summary_auto_delete"
294            android:title="&#64;string/pref_title_auto_delete"
295            android:defaultValue="false"... />
296        &lt;Preference
297            android:key="pref_key_sms_delete_limit"
298            android:dependency="pref_key_auto_delete"
299            android:summary="&#64;string/pref_summary_delete_limit"
300            android:title="&#64;string/pref_title_sms_delete"... />
301        &lt;Preference
302            android:key="pref_key_mms_delete_limit"
303            android:dependency="pref_key_auto_delete"
304            android:summary="&#64;string/pref_summary_delete_limit"
305            android:title="&#64;string/pref_title_mms_delete" ... />
306    &lt;/PreferenceCategory>
307    ...
308&lt;/PreferenceScreen>
309</pre>
310
311
312<h4 id="Subscreens">使用子屏幕</h4>
313
314<p>若要将设置组放入子屏幕(如图 3 所示),请将
315{@link android.preference.Preference} 对象组放入 {@link
316android.preference.PreferenceScreen} 内。</p>
317
318<img src="{@docRoot}images/ui/settings/settings-subscreen.png" alt="" />
319<p class="img-caption"><strong>图 3. </strong>设置子屏幕。{@code
320&lt;PreferenceScreen&gt;} 元素创建的项目选中后,即会打开一个单独的列表来显示嵌套设置。
321</p>
322
323<p>例如:</p>
324
325<pre>
326&lt;PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
327    &lt;!-- opens a subscreen of settings -->
328    &lt;PreferenceScreen
329        android:key="button_voicemail_category_key"
330        android:title="&#64;string/voicemail"
331        android:persistent="false">
332        &lt;ListPreference
333            android:key="button_voicemail_provider_key"
334            android:title="&#64;string/voicemail_provider" ... />
335        &lt;!-- opens another nested subscreen -->
336        &lt;PreferenceScreen
337            android:key="button_voicemail_setting_key"
338            android:title="&#64;string/voicemail_settings"
339            android:persistent="false">
340            ...
341        &lt;/PreferenceScreen>
342        &lt;RingtonePreference
343            android:key="button_voicemail_ringtone_key"
344            android:title="&#64;string/voicemail_ringtone_title"
345            android:ringtoneType="notification" ... />
346        ...
347    &lt;/PreferenceScreen>
348    ...
349&lt;/PreferenceScreen>
350</pre>
351
352
353<h3 id="Intents">使用 Intent </h3>
354
355<p>在某些情况下,您可能需要首选项来打开不同的 Activity(而不是 Web
356浏览器等设置屏幕)或查看网页。要在用户选择首选项时调用 {@link
357android.content.Intent},请将{@code &lt;intent&gt;}
358元素添加为相应 {@code &lt;Preference&gt;} 元素的子元素。</p>
359
360<p>例如,您可以按如下方法使用首选项打开网页:</p>
361
362<pre>
363&lt;Preference android:title="@string/prefs_web_page" >
364    &lt;intent android:action="android.intent.action.VIEW"
365            android:data="http://www.example.com" />
366&lt;/Preference>
367</pre>
368
369<p>您可以使用以下属性创建隐式和显式 Intent:</p>
370
371<dl>
372  <dt>{@code android:action}</dt>
373    <dd>要分配的操作(按照 {@link android.content.Intent#setAction setAction()}
374方法)。</dd>
375  <dt>{@code android:data}</dt>
376    <dd>要分配的数据(按照 {@link android.content.Intent#setData setData()} 方法)。</dd>
377  <dt>{@code android:mimeType}</dt>
378    <dd>要分配的 MIME 类型(按照 {@link android.content.Intent#setType setType()}
379方法)。</dd>
380  <dt>{@code android:targetClass}</dt>
381    <dd>组件名称的类部分(按照 {@link android.content.Intent#setComponent
382setComponent()} 方法)。</dd>
383  <dt>{@code android:targetPackage}</dt>
384    <dd>组件名称的软件包部分(按照 {@link
385android.content.Intent#setComponent setComponent()} 方法)。</dd>
386</dl>
387
388
389
390<h2 id="Activity">创建首选项 Activity</h2>
391
392<p>要在 Activity 中显示您的设置,请扩展 {@link
393android.preference.PreferenceActivity} 类。这是传统 {@link
394android.app.Activity} 类的扩展,该类根据 {@link
395android.preference.Preference} 对象的层次结构显示设置列表。当用户进行更改时,{@link android.preference.PreferenceActivity}
396会自动保留与每个 {@link
397android.preference.Preference} 相关的设置。</p>
398
399<p class="note"><strong>注:</strong>如果您是开发针对 Android 3.0 及
400更高版本系统的应用,则应改为使用 {@link android.preference.PreferenceFragment}。转到下文有关<a href="#Fragment">使用首选项片段</a>的部分。
401</p>
402
403<p>请记住最重要的一点,就是不要在 {@link
404android.preference.PreferenceActivity#onCreate onCreate()} 回调期间加载视图的布局。相反,请调用 {@link
405android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} 以将在
406XML 文件中声明的首选项添加到 Activity。例如,一个能够正常工作的
407{@link android.preference.PreferenceActivity} 至少需要如下代码:</p>
408
409<pre>
410public class SettingsActivity extends PreferenceActivity {
411    &#64;Override
412    public void onCreate(Bundle savedInstanceState) {
413        super.onCreate(savedInstanceState);
414        addPreferencesFromResource(R.xml.preferences);
415    }
416}
417</pre>
418
419<p>实际上,对于某些应用而言此,代码就已足够,因为用户修改某首选项后,系统会立即将所做的更改保存到默认 {@link android.content.SharedPreferences} 文件中,如需检查用户的设置,可以使用您的其他应用组件读取该文件。
420
421不过,许多应用需要的代码要稍微多一点,以侦听首选项发生的变化。有关侦听
422{@link android.content.SharedPreferences}
423文件变化的信息,请参阅<a href="#ReadingPrefs">读取首选项</a>部分。
424</p>
425
426
427
428
429<h2 id="Fragment">使用首选项片段</h2>
430
431<p>如果您是开发针对 Android 3.0(API 级别 11)及更高版本系统的应用,则应使用 {@link
432android.preference.PreferenceFragment} 显示 {@link android.preference.Preference}
433对象的列表。您可以将 {@link android.preference.PreferenceFragment} 添加到任何 Activity,而不必使用
434{@link android.preference.PreferenceActivity}。</p>
435
436<p>与仅使用上述 Activity 相比,无论您在构建何种 Activity,<a href="{@docRoot}guide/components/fragments.html">片段</a>都可为应用提供一个更加灵活的体系结构。
437
438因此,我们建议您尽可能使用 {@link
439android.preference.PreferenceFragment} 控制设置的显示,而不是使用 {@link
440android.preference.PreferenceActivity}。</p>
441
442<p>{@link android.preference.PreferenceFragment} 的实现就像定义
443{@link android.preference.PreferenceFragment#onCreate onCreate()} 方法以使用
444{@link android.preference.PreferenceFragment#addPreferencesFromResource
445addPreferencesFromResource()} 加载首选项文件一样简单。例如:</p>
446
447<pre>
448public static class SettingsFragment extends PreferenceFragment {
449    &#64;Override
450    public void onCreate(Bundle savedInstanceState) {
451        super.onCreate(savedInstanceState);
452
453        // Load the preferences from an XML resource
454        addPreferencesFromResource(R.xml.preferences);
455    }
456    ...
457}
458</pre>
459
460<p>然后,正如您对其他任何
461{@link android.app.Fragment} 的处理一样,您可以将此片段添加到 {@link android.app.Activity}。例如:</p>
462
463<pre>
464public class SettingsActivity extends Activity {
465    &#64;Override
466    protected void onCreate(Bundle savedInstanceState) {
467        super.onCreate(savedInstanceState);
468
469        // Display the fragment as the main content.
470        getFragmentManager().beginTransaction()
471                .replace(android.R.id.content, new SettingsFragment())
472                .commit();
473    }
474}
475</pre>
476
477<p class="note"><strong>注:</strong>{@link android.preference.PreferenceFragment} 没有自己的
478{@link android.content.Context} 对象。如需 {@link android.content.Context}
479对象,您可以调用 {@link android.app.Fragment#getActivity()}。但请注意,只应在该片段附加到 Activity 时才调用
480{@link android.app.Fragment#getActivity()}。如果该片段尚未附加或在其生命周期结束期间已分离,则
481{@link
482android.app.Fragment#getActivity()} 将返回空 null。</p>
483
484
485<h2 id="Defaults">设置默认值</h2>
486
487<p>您创建的首选项可能会为应用定义一些重要行为,因此在用户首次打开应用时,您有必要使用每个 {@link android.preference.Preference}
488的默认值初始化相关的
489{@link android.content.SharedPreferences}
490文件。</p>
491
492<p>首先,您必须使用 {@code android:defaultValue} 属性为 XML 文件中的每个 {@link
493android.preference.Preference}
494对象指定默认值。该值可以是适合相应
495{@link android.preference.Preference} 对象的任意数据类型。例如:
496</p>
497
498<pre>
499&lt;!-- default value is a boolean -->
500&lt;CheckBoxPreference
501    android:defaultValue="true"
502    ... />
503
504&lt;!-- default value is a string -->
505&lt;ListPreference
506    android:defaultValue="@string/pref_syncConnectionTypes_default"
507    ... />
508</pre>
509
510<p>然后,通过应用的主 Activity(以及用户首次进入应用所藉由的任何其他 Activity)中的 {@link android.app.Activity#onCreate onCreate()}
511方法调用 {@link android.preference.PreferenceManager#setDefaultValues
512setDefaultValues()}:
513</p>
514
515<pre>
516PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);
517</pre>
518
519<p>在
520{@link android.app.Activity#onCreate onCreate()}
521期间调用此方法可确保使用默认设置正确初始化应用,而应用可能需要读取这些设置以确定某些行为(例如,是否在蜂窝网络中下载数据)。
522</p>
523
524<p>此方法采用三个参数:</p>
525<ul>
526  <li>应用 {@link android.content.Context}。</li>
527  <li>要为其设置默认值的首选项 XML 文件的资源 ID。</li>
528  <li>一个布尔值,用于指示是否应该多次设置默认值。
529<p>如果该值为 <code>false</code>,则仅当过去从未调用此方法时(或者默认值共享首选项文件中的
530{@link android.preference.PreferenceManager#KEY_HAS_SET_DEFAULT_VALUES}为
531false 时),系统才会设置默认值。</p></li>
532</ul>
533
534<p>只要将第三个参数设置为
535<code>false</code>,您便可在每次启动 Activity 时安全地调用此方法,而不必通过重置为默认值来替代用户已保存的首选项。
536但是,如果将它设置为
537<code>true</code>,则需要使用默认值替代之前的所有值。</p>
538
539
540
541<h2 id="PreferenceHeaders">使用首选项标头</h2>
542
543<p>在极少数情况下,您可能需要设计设置,使第一个屏幕仅显示<a href="#Subscreens">子屏幕</a>的列表(例如在系统“设置”应用中,如图 4 和图 5 所示)。
544
545在开发针对 Android 3.0 及更高版本系统的此类设计时,您应该使用
546Android 3.0 中的新“标头”功能,而非使用嵌套的
547{@link android.preference.PreferenceScreen} 元素构建子屏幕。</p>
548
549<p>要使用标头构建设置,您需要:</p>
550<ol>
551  <li>将每组设置分成单独的 {@link
552android.preference.PreferenceFragment} 实例。即,每组设置均需要一个单独的 XML
553文件。</li>
554  <li>创建 XML
555标头文件,其中列出每个设置组并声明哪个片段包含对应的设置列表。</li>
556  <li>扩展 {@link android.preference.PreferenceActivity} 类以托管设置。</li>
557  <li>实现 {@link
558android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()}
559回调以指定标头文件。</li>
560</ol>
561
562<p>使用此设计的一大好处是,在大屏幕上运行时,{@link android.preference.PreferenceActivity}
563会自动提供双窗格布局(如图 4 所示)。</p>
564
565<p>即使您的应用支持早于 3.0 的 Android 版本,您仍可将应用设计为使用
566{@link android.preference.PreferenceFragment}
567在较新版本的设备上呈现双窗格布局,同时仍支持较旧版本设备上传统的多屏幕层次结构(请参阅<a href="#BackCompatHeaders">使用首选项标头支持旧版本</a>部分)。
568
569</p>
570
571<img src="{@docRoot}images/ui/settings/settings-headers-tablet.png" alt="" />
572<p class="img-caption"><strong>图 4. </strong>带标头的双窗格布局。 <br/><b>1.</b>标头用
573XML 标头文件定义。 <br/><b>2.</b>每组设置均由
574{@link android.preference.PreferenceFragment}(通过标头文件中的 {@code &lt;header&gt;}
575元素指定)定义。</p>
576
577<img src="{@docRoot}images/ui/settings/settings-headers-handset.png" alt="" />
578<p class="img-caption"><strong>图 5. </strong>带设置标头的手机设备。选择项目后,相关的
579{@link android.preference.PreferenceFragment}
580将替换标头。</p>
581
582
583<h3 id="CreateHeaders" style="clear:left">创建标头文件</h3>
584
585<p>标头列表中的每组设置均由根 {@code &lt;preference-headers&gt;} 元素内的单个 {@code &lt;header&gt;}
586元素指定。例如:</p>
587
588<pre>
589&lt;?xml version="1.0" encoding="utf-8"?>
590&lt;preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
591    &lt;header
592        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
593        android:title="@string/prefs_category_one"
594        android:summary="@string/prefs_summ_category_one" />
595    &lt;header
596        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
597        android:title="@string/prefs_category_two"
598        android:summary="@string/prefs_summ_category_two" >
599        &lt;!-- key/value pairs can be included as arguments for the fragment. -->
600        &lt;extra android:name="someKey" android:value="someHeaderValue" />
601    &lt;/header>
602&lt;/preference-headers>
603</pre>
604
605<p>每个标头均可使用 {@code android:fragment} 属性声明在用户选择该标头时应打开的 {@link
606android.preference.PreferenceFragment} 实例。</p>
607
608<p>{@code &lt;extras&gt;} 元素允许您使用 {@link
609android.os.Bundle} 将键值对传递给片段。该片段可以通过调用 {@link
610android.app.Fragment#getArguments()} 检索参数。您向该片段传递参数的原因可能有很多,不过一个重要原因是,要对每个组重复使用
611{@link
612android.preference.PreferenceFragment}
613的相同子类,而且要使用参数来指定该片段应加载哪些首选项 XML 文件。</p>
614
615<p>例如,当每个标头均使用
616{@code "settings"} 键定义 {@code &lt;extra&gt;} 参数时,则可以对多个设置组重复使用以下片段:</p>
617
618<pre>
619public static class SettingsFragment extends PreferenceFragment {
620    &#64;Override
621    public void onCreate(Bundle savedInstanceState) {
622        super.onCreate(savedInstanceState);
623
624        String settings = getArguments().getString("settings");
625        if ("notifications".equals(settings)) {
626            addPreferencesFromResource(R.xml.settings_wifi);
627        } else if ("sync".equals(settings)) {
628            addPreferencesFromResource(R.xml.settings_sync);
629        }
630    }
631}
632</pre>
633
634
635
636<h3 id="DisplayHeaders">显示标头</h3>
637
638<p>要显示首选项标头,您必须实现 {@link
639android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} 回调方法并调用
640{@link android.preference.PreferenceActivity#loadHeadersFromResource
641loadHeadersFromResource()}。例如:</p>
642
643<pre>
644public class SettingsActivity extends PreferenceActivity {
645    &#64;Override
646    public void onBuildHeaders(List&lt;Header> target) {
647        loadHeadersFromResource(R.xml.preference_headers, target);
648    }
649}
650</pre>
651
652<p>当用户从标头列表中选择一个项目时,系统会打开相关的 {@link
653android.preference.PreferenceFragment}。</p>
654
655<p class="note"><strong>注:</strong>使用首选项标头时,{@link
656android.preference.PreferenceActivity} 的子类无需实现 {@link
657android.preference.PreferenceActivity#onCreate onCreate()}
658方法,因为 Activity 唯一所需执行的任务就是加载标头。</p>
659
660
661<h3 id="BackCompatHeaders">使用首选项标头支持旧版本</h3>
662
663<p>如果您的应用支持早于 3.0 的 Android
664版本,则在 Android 3.0 及更高版本系统上运行时,您仍可使用标头提供双窗格数据。为此,您只需另外创建
665一个使用基本 {@link android.preference.Preference
666&lt;Preference&gt;} 元素的首选项 XML 文件即可,这些基本元素的行为方式与标头项目类似(供较旧版本的 Android
667 系统使用)。</p>
668
669<p>但是,每个 {@link
670android.preference.Preference &lt;Preference&gt;} 元素均会向 {@link android.preference.PreferenceActivity} 发送一个 {@link android.content.Intent},指定要加载哪个首选项 XML 文件,而不是打开新的
671{@link android.preference.PreferenceScreen}。
672</p>
673
674<p>例如,下面就是一个用于 Android 3.0
675及更高版本系统的首选项标头 XML 文件 ({@code res/xml/preference_headers.xml}):</p>
676
677<pre>
678&lt;preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
679    &lt;header
680        android:fragment="com.example.prefs.SettingsFragmentOne"
681        android:title="@string/prefs_category_one"
682        android:summary="@string/prefs_summ_category_one" />
683    &lt;header
684        android:fragment="com.example.prefs.SettingsFragmentTwo"
685        android:title="@string/prefs_category_two"
686        android:summary="@string/prefs_summ_category_two" />
687&lt;/preference-headers>
688</pre>
689
690<p>下面是为早于
691Android 3.0 版本的系统提供相同标头的首选项文件 ({@code res/xml/preference_headers_legacy.xml}):</p>
692
693<pre>
694&lt;PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
695    &lt;Preference
696        android:title="@string/prefs_category_one"
697        android:summary="@string/prefs_summ_category_one"  >
698        &lt;intent
699            android:targetPackage="com.example.prefs"
700            android:targetClass="com.example.prefs.SettingsActivity"
701            android:action="com.example.prefs.PREFS_ONE" />
702    &lt;/Preference>
703    &lt;Preference
704        android:title="@string/prefs_category_two"
705        android:summary="@string/prefs_summ_category_two" >
706        &lt;intent
707            android:targetPackage="com.example.prefs"
708            android:targetClass="com.example.prefs.SettingsActivity"
709            android:action="com.example.prefs.PREFS_TWO" />
710    &lt;/Preference>
711&lt;/PreferenceScreen>
712</pre>
713
714<p>由于是从 Android 3.0 开始方添加对 {@code &lt;preference-headers&gt;} 的支持,因此只有在 Androd 3.0 或更高版本中运行时,系统才会在您的 {@link
715android.preference.PreferenceActivity}
716中调用 {@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()}。要加载“旧版”标头文件
717({@code preference_headers_legacy.xml}),您必须检查 Android
718版本,如果版本低于 Android 3.0 ({@link
719android.os.Build.VERSION_CODES#HONEYCOMB}),请调用 {@link
720android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()}
721来加载旧版标头文件。例如:</p>
722
723<pre>
724&#64;Override
725public void onCreate(Bundle savedInstanceState) {
726    super.onCreate(savedInstanceState);
727    ...
728
729    if (Build.VERSION.SDK_INT &lt; Build.VERSION_CODES.HONEYCOMB) {
730        // Load the legacy preferences headers
731        addPreferencesFromResource(R.xml.preference_headers_legacy);
732    }
733}
734
735// Called only on Honeycomb and later
736&#64;Override
737public void onBuildHeaders(List&lt;Header> target) {
738   loadHeadersFromResource(R.xml.preference_headers, target);
739}
740</pre>
741
742<p>最后要做的就是处理传入 Activity 的
743{@link android.content.Intent},以确定要加载的首选项文件。因此,请检索 Intent 的操作,并将其与在首选项 XML 的 {@code &lt;intent&gt;}
744标记中使用的已知操作字符串进行比较。</p>
745
746<pre>
747final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE";
748...
749
750&#64;Override
751public void onCreate(Bundle savedInstanceState) {
752    super.onCreate(savedInstanceState);
753
754    String action = getIntent().getAction();
755    if (action != null &amp;&amp; action.equals(ACTION_PREFS_ONE)) {
756        addPreferencesFromResource(R.xml.preferences);
757    }
758    ...
759
760    else if (Build.VERSION.SDK_INT &lt; Build.VERSION_CODES.HONEYCOMB) {
761        // Load the legacy preferences headers
762        addPreferencesFromResource(R.xml.preference_headers_legacy);
763    }
764}
765</pre>
766
767<p>值得注意的是,连续调用 {@link
768android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()}
769会将所有首选项堆叠在一个列表中,因此请将条件与
770else-if 语句链接在一起,确保它只调用一次。</p>
771
772
773
774
775
776<h2 id="ReadingPrefs">读取首选项</h2>
777
778<p>默认情况下,应用的所有首选项均保存到一个可通过调用静态方法
779{@link
780android.preference.PreferenceManager#getDefaultSharedPreferences
781PreferenceManager.getDefaultSharedPreferences()} 从应用内的任何位置访问的文件中。这将返回 {@link
782android.content.SharedPreferences} 对象,其中包含与
783{@link
784android.preference.PreferenceActivity} 中所用 {@link android.preference.Preference} 对象相关的所有键值对。</p>
785
786<p>例如,从应用中的任何其他 Activity 读取某个首选项值的方法如下:
787</p>
788
789<pre>
790SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
791String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");
792</pre>
793
794
795
796<h3 id="Listening">侦听首选项变更</h3>
797
798<p>出于某些原因,您可能希望在用户更改任一首选项时立即收到通知。
799要在任一首选项发生更改时收到回调,请实现
800{@link android.content.SharedPreferences.OnSharedPreferenceChangeListener
801SharedPreference.OnSharedPreferenceChangeListener} 接口,并通过调用 {@link
802android.content.SharedPreferences#registerOnSharedPreferenceChangeListener
803registerOnSharedPreferenceChangeListener()} 为
804{@link android.content.SharedPreferences} 对象注册侦听器。</p>
805
806<p>该接口只有 {@link
807android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged
808onSharedPreferenceChanged()}
809一种回调方法,而且您可能会发现在 Activity 过程中实现该接口最为简单。例如:</p>
810
811<pre>
812public class SettingsActivity extends PreferenceActivity
813                              implements OnSharedPreferenceChangeListener {
814    public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
815    ...
816
817    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
818        String key) {
819        if (key.equals(KEY_PREF_SYNC_CONN)) {
820            Preference connectionPref = findPreference(key);
821            // Set summary to be the user-description for the selected value
822            connectionPref.setSummary(sharedPreferences.getString(key, ""));
823        }
824    }
825}
826</pre>
827
828<p>在此示例中,该方法检查更改的设置是否是针对已知的首选项键。它调用
829{@link android.preference.PreferenceActivity#findPreference findPreference()} 来获取已更改的
830{@link android.preference.Preference}
831对象,以便能够将项目摘要修改为对用户选择的说明。即,如果设置为 {@link
832android.preference.ListPreference} 或其他多选设置时,则当设置更改为显示当前状态(例如,图
8335 所示的“Sleep”设置)时,您应调用 {@link
834android.preference.Preference#setSummary setSummary()}。</p>
835
836<p class="note"><strong>注:</strong>正如 Android 设计有关<a href="{@docRoot}design/patterns/settings.html">设置</a>的文档中所述,我们建议您在用户每次更改首选项时更新
837{@link android.preference.ListPreference}
838的摘要,以描述当前设置。</p>
839
840<p>若要妥善管理 Activity 生命周期,我们建议您在
841{@link
842android.app.Activity#onResume} 和 {@link android.app.Activity#onPause} 回调期间分别注册和注销 {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener}。</p>
843
844<pre>
845&#64;Override
846protected void onResume() {
847    super.onResume();
848    getPreferenceScreen().getSharedPreferences()
849            .registerOnSharedPreferenceChangeListener(this);
850}
851
852&#64;Override
853protected void onPause() {
854    super.onPause();
855    getPreferenceScreen().getSharedPreferences()
856            .unregisterOnSharedPreferenceChangeListener(this);
857}
858</pre>
859
860<p class="caution"><strong>注意:</strong>目前,首选项管理器不会在您调用 {@link
861android.content.SharedPreferences#registerOnSharedPreferenceChangeListener
862registerOnSharedPreferenceChangeListener()}
863时存储对侦听器的强引用。但是,您必须存储对侦听器的强引用,否则它将很容易被当作垃圾回收。
864我们建议您将对侦听器的引用保存在只要您需要侦听器就会存在的对象的实例数据中。
865
866</p>
867
868<p>例如,在以下代码中,调用方未保留对侦听器的引用。
869因此,侦听器将容易被当作垃圾回收,并在将来某个不确定的时间失败:
870</p>
871
872<pre>
873prefs.registerOnSharedPreferenceChangeListener(
874  // Bad! The listener is subject to garbage collection!
875  new SharedPreferences.OnSharedPreferenceChangeListener() {
876  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
877    // listener implementation
878  }
879});
880</pre>
881
882<p>有鉴于此,请将对侦听器的引用存储在只要需要侦听器就会存在的对象的实例数据字段中:
883</p>
884
885<pre>
886SharedPreferences.OnSharedPreferenceChangeListener listener =
887    new SharedPreferences.OnSharedPreferenceChangeListener() {
888  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
889    // listener implementation
890  }
891};
892prefs.registerOnSharedPreferenceChangeListener(listener);
893</pre>
894
895<h2 id="NetworkUsage">管理网络使用情况</h2>
896
897
898<p>从 Android 4.0
899开始,通过系统的“设置”应用,用户可以了解自己的应用在前台和后台使用的网络数据量。然后,用户可以据此禁止具体的应用使用后台数据。
900为了避免用户禁止您的应用从后台访问数据,您应该有效地使用数据连接,并允许用户通过应用设置优化应用的数据使用。
901
902<p>
903
904<p>例如,您可以允许用户控制应用同步数据的频率,控制应用是否仅在有
905Wi-Fi 时才执行上传/下载操作,以及控制应用能否在漫游时使用数据,等等。为用户提供这些控件后,即使数据使用量接近他们在系统“设置”中设置的限制,他们也不大可能禁止您的应用访问数据,因为他们可以精确地控制应用使用的数据量。
906
907
908</p>
909
910<p>在 {@link android.preference.PreferenceActivity}
911中添加必要的首选项来控制应用的数据使用习惯后,您应立即在清单文件中为 {@link
912android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} 添加 Intent 过滤器。例如:</p>
913
914<pre>
915&lt;activity android:name="SettingsActivity" ... >
916    &lt;intent-filter>
917       &lt;action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
918       &lt;category android:name="android.intent.category.DEFAULT" />
919    &lt;/intent-filter>
920&lt;/activity>
921</pre>
922
923<p>此 Intent
924过滤器指示系统此 Activity 控制应用的数据使用情况。因此,当用户从系统的“设置”应用检查应用所使用的数据量时,可以使用“查看应用设置”按钮启动
925{@link android.preference.PreferenceActivity},这样,用户就能够优化应用使用的数据量。
926
927<em></em></p>
928
929
930
931
932
933
934
935<h2 id="Custom">构建自定义首选项</h2>
936
937<p>Android 框架包括各种
938{@link android.preference.Preference}
939子类,您可以使用它们为各种不同类型的设置构建 UI。不过,您可能会发现自己需要的设置没有内置解决方案,例如,数字选取器或日期选取器。
940在这种情况下,您将需要通过扩展
941{@link android.preference.Preference} 类或其他子类之一来创建自定义首选项。</p>
942
943<p>扩展 {@link android.preference.Preference}
944类时,您需要执行以下几项重要操作:</p>
945
946<ul>
947  <li>指定在用户选择设置时显示的用户界面。</li>
948  <li>适时保存设置的值。</li>
949  <li>使用显示的当前(默认)值初始化
950{@link android.preference.Preference}。</li>
951  <li>在系统请求时提供默认值。</li>
952  <li>如果 {@link android.preference.Preference}
953提供自己的 UI(例如对话框),请保存并恢复状态以处理生命周期变更(例如,用户旋转屏幕)。</li>
954</ul>
955
956<p>下文介绍如何完成所有这些任务。</p>
957
958
959
960<h3 id="CustomSelected">指定用户界面</h3>
961
962  <p>如果您要直接扩展 {@link android.preference.Preference} 类,则需要实现
963{@link android.preference.Preference#onClick()}
964来定义在用户选择该项时发生的操作。不过,大多数自定义设置都会扩展 {@link android.preference.DialogPreference}
965以显示对话框,从而简化这一过程。扩展 {@link
966android.preference.DialogPreference} 时,必须在类构造函数中调用 {@link
967android.preference.DialogPreference#setDialogLayoutResource setDialogLayoutResourcs()}
968来指定对话框的布局。</p>
969
970  <p>例如,自定义 {@link
971android.preference.DialogPreference}
972可以使用下面的构造函数来声明布局并为默认的肯定和否定对话框按钮指定文本:</p>
973
974<pre>
975public class NumberPickerPreference extends DialogPreference {
976    public NumberPickerPreference(Context context, AttributeSet attrs) {
977        super(context, attrs);
978
979        setDialogLayoutResource(R.layout.numberpicker_dialog);
980        setPositiveButtonText(android.R.string.ok);
981        setNegativeButtonText(android.R.string.cancel);
982
983        setDialogIcon(null);
984    }
985    ...
986}
987</pre>
988
989
990
991<h3 id="CustomSave">保存设置的值</h3>
992
993<p>如果设置的值为整型数或是用于保存布尔值的
994{@link android.preference.Preference#persistBoolean persistBoolean()},则可通过调用 {@link
995android.preference.Preference} 类的一个 {@code persist*()} 方法(如 {@link
996android.preference.Preference#persistInt persistInt()})随时保存该值。</p>
997
998<p class="note"><strong>注:</strong>每个 {@link android.preference.Preference}
999均只能保存一种数据类型,因此您必须使用适合自定义
1000{@link android.preference.Preference} 所用数据类型的 {@code persist*()} 方法。</p>
1001
1002<p>至于何时选择保留设置,则可能取决于要扩展的 {@link
1003android.preference.Preference} 类。如果扩展
1004{@link
1005android.preference.DialogPreference},则只能在对话框因肯定结果(用户选择“确定”按钮)而关闭时保留该值。</p>
1006
1007<p>当 {@link android.preference.DialogPreference} 关闭时,系统会调用 {@link
1008android.preference.DialogPreference#onDialogClosed onDialogClosed()} 方法。该方法包括一个布尔参数,用于指定用户结果是否为“肯定”;如果值为
1009<code>true</code>,则表示用户选择的是肯定按钮且您应该保存新值。
1010例如:
1011</p>
1012
1013<pre>
1014&#64;Override
1015protected void onDialogClosed(boolean positiveResult) {
1016    // When the user selects "OK", persist the new value
1017    if (positiveResult) {
1018        persistInt(mNewValue);
1019    }
1020}
1021</pre>
1022
1023<p>在此示例中,<code>mNewValue</code>
1024是一个类成员,可存放设置的当前值。调用 {@link android.preference.Preference#persistInt persistInt()} 会将该值保存到
1025{@link android.content.SharedPreferences} 文件(自动使用在此
1026{@link android.preference.Preference} 的 XML 文件中指定的键)。</p>
1027
1028
1029<h3 id="CustomInitialize">初始化当前值</h3>
1030
1031<p>系统将 {@link android.preference.Preference} 添加到屏幕时,会调用
1032{@link android.preference.Preference#onSetInitialValue onSetInitialValue()}
1033来通知您设置是否具有保留值。如果没有保留值,则此调用将为您提供默认值。
1034</p>
1035
1036<p>{@link android.preference.Preference#onSetInitialValue onSetInitialValue()}
1037方法传递一个布尔值 (<code>restorePersistedValue</code>),以指示是否已为该设置保留值。
1038如果值为 <code>true</code>,则应通过调用
1039{@link
1040android.preference.Preference} 类的一个 {@code getPersisted*()} 方法(如整型值对应的 {@link
1041android.preference.Preference#getPersistedInt getPersistedInt()})来检索保留值。通常,您会需要检索保留值,以便能够正确更新 UI 来反映之前保存的值。
1042
1043</p>
1044
1045<p>如果 <code>restorePersistedValue</code> 为
1046<code>false</code>,则应使用在第二个参数中传递的默认值。</p>
1047
1048<pre>
1049&#64;Override
1050protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
1051    if (restorePersistedValue) {
1052        // Restore existing state
1053        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
1054    } else {
1055        // Set default state from the XML attribute
1056        mCurrentValue = (Integer) defaultValue;
1057        persistInt(mCurrentValue);
1058    }
1059}
1060</pre>
1061
1062<p>每种 {@code getPersisted*()}
1063方法均采用一个参数,用于指定在实际上没有保留值或该键不存在时所要使用的默认值。在上述示例中,当
1064{@link
1065android.preference.Preference#getPersistedInt getPersistedInt()} 不能返回保留值时,局部常量用于指定默认值。</p>
1066
1067<p class="caution"><strong>注意:</strong>您<strong>不能</strong>使用
1068<code>defaultValue</code> 作为 {@code getPersisted*()} 方法中的默认值,因为当
1069<code>restorePersistedValue</code> 为 <code>true</code> 时,其值始终为 null。</p>
1070
1071
1072<h3 id="CustomDefault">提供默认值</h3>
1073
1074<p>如果 {@link android.preference.Preference}
1075类的实例指定一个默认值(使用 {@code android:defaultValue}
1076属性),则在实例化对象以检索该值时,系统会调用 {@link android.preference.Preference#onGetDefaultValue
1077onGetDefaultValue()}。您必须实现此方法,系统才能将默认值保存在 {@link
1078android.content.SharedPreferences} 中。
1079例如:</p>
1080
1081<pre>
1082&#64;Override
1083protected Object onGetDefaultValue(TypedArray a, int index) {
1084    return a.getInteger(index, DEFAULT_VALUE);
1085}
1086</pre>
1087
1088<p>方法参数可提供您所需的一切:属性的数组和
1089{@code android:defaultValue}(必须检索的值)的索引位置。之所以必须实现此方法以从该属性中提取默认值,是因为您必须为此属性指定在未定义属性值时所要使用的局部默认值。
1090
1091</p>
1092
1093
1094
1095<h3 id="CustomSaveState">保存和恢复首选项的状态</h3>
1096
1097<p>正如布局中的 {@link android.view.View}
1098一样,在重启 Activity 或片段时(例如,用户旋转屏幕),{@link android.preference.Preference}
1099子类也负责保存并恢复其状态。要正确保存并恢复
1100{@link android.preference.Preference} 类的状态,您必须实现生命周期回调方法
1101{@link android.preference.Preference#onSaveInstanceState
1102onSaveInstanceState()} 和 {@link
1103android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()}。</p>
1104
1105<p>{@link android.preference.Preference} 的状态由实现
1106{@link android.os.Parcelable} 接口的对象定义。Android 框架为您提供此类对象,作为定义状态对象({@link
1107android.preference.Preference.BaseSavedState}
1108类)的起点。</p>
1109
1110<p>要定义 {@link android.preference.Preference} 类保存其状态的方式,您应该扩展
1111{@link android.preference.Preference.BaseSavedState} 类。您只需重写几种方法并定义
1112{@link android.preference.Preference.BaseSavedState#CREATOR}
1113对象。</p>
1114
1115<p>对于大多数应用,如果 {@link android.preference.Preference}
1116子类保存除整型数以外的其他数据类型,则可复制下列实现并直接更改处理
1117{@code value} 的行。</p>
1118
1119<pre>
1120private static class SavedState extends BaseSavedState {
1121    // Member that holds the setting's value
1122    // Change this data type to match the type saved by your Preference
1123    int value;
1124
1125    public SavedState(Parcelable superState) {
1126        super(superState);
1127    }
1128
1129    public SavedState(Parcel source) {
1130        super(source);
1131        // Get the current preference's value
1132        value = source.readInt();  // Change this to read the appropriate data type
1133    }
1134
1135    &#64;Override
1136    public void writeToParcel(Parcel dest, int flags) {
1137        super.writeToParcel(dest, flags);
1138        // Write the preference's value
1139        dest.writeInt(value);  // Change this to write the appropriate data type
1140    }
1141
1142    // Standard creator object using an instance of this class
1143    public static final Parcelable.Creator&lt;SavedState> CREATOR =
1144            new Parcelable.Creator&lt;SavedState>() {
1145
1146        public SavedState createFromParcel(Parcel in) {
1147            return new SavedState(in);
1148        }
1149
1150        public SavedState[] newArray(int size) {
1151            return new SavedState[size];
1152        }
1153    };
1154}
1155</pre>
1156
1157<p>如果将上述 {@link android.preference.Preference.BaseSavedState}
1158实现添加到您的应用(通常,作为 {@link android.preference.Preference}
1159子类的子类),则需要为
1160{@link android.preference.Preference} 子类实现 {@link android.preference.Preference#onSaveInstanceState
1161onSaveInstanceState()} 和 {@link
1162android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()} 方法。</p>
1163
1164<p>例如:</p>
1165
1166<pre>
1167&#64;Override
1168protected Parcelable onSaveInstanceState() {
1169    final Parcelable superState = super.onSaveInstanceState();
1170    // Check whether this Preference is persistent (continually saved)
1171    if (isPersistent()) {
1172        // No need to save instance state since it's persistent,
1173        // use superclass state
1174        return superState;
1175    }
1176
1177    // Create instance of custom BaseSavedState
1178    final SavedState myState = new SavedState(superState);
1179    // Set the state's value with the class member that holds current
1180    // setting value
1181    myState.value = mNewValue;
1182    return myState;
1183}
1184
1185&#64;Override
1186protected void onRestoreInstanceState(Parcelable state) {
1187    // Check whether we saved the state in onSaveInstanceState
1188    if (state == null || !state.getClass().equals(SavedState.class)) {
1189        // Didn't save the state, so call superclass
1190        super.onRestoreInstanceState(state);
1191        return;
1192    }
1193
1194    // Cast state to custom BaseSavedState and pass to superclass
1195    SavedState myState = (SavedState) state;
1196    super.onRestoreInstanceState(myState.getSuperState());
1197
1198    // Set this Preference's widget to reflect the restored state
1199    mNumberPicker.setValue(myState.value);
1200}
1201</pre>
1202
1203