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 <CheckBoxPreference>})匹配的 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<PreferenceScreen>} 元素。您可以在此元素内添加每个 {@link 197android.preference.Preference}。在 198{@link android.preference.PreferenceScreen <PreferenceScreen>} 199元素内添加的每个子项均将作为单独的项目显示在设置列表中。</p> 200 201<p>例如:</p> 202 203<pre> 204<?xml version="1.0" encoding="utf-8"?> 205<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 206 <CheckBoxPreference 207 android:key="pref_sync" 208 android:title="@string/pref_sync" 209 android:summary="@string/pref_sync_summ" 210 android:defaultValue="true" /> 211 <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</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 <intent>}</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 <PreferenceCategory>} 元素指定。 <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<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 288 <PreferenceCategory 289 android:title="@string/pref_sms_storage_title" 290 android:key="pref_key_storage_settings"> 291 <CheckBoxPreference 292 android:key="pref_key_auto_delete" 293 android:summary="@string/pref_summary_auto_delete" 294 android:title="@string/pref_title_auto_delete" 295 android:defaultValue="false"... /> 296 <Preference 297 android:key="pref_key_sms_delete_limit" 298 android:dependency="pref_key_auto_delete" 299 android:summary="@string/pref_summary_delete_limit" 300 android:title="@string/pref_title_sms_delete"... /> 301 <Preference 302 android:key="pref_key_mms_delete_limit" 303 android:dependency="pref_key_auto_delete" 304 android:summary="@string/pref_summary_delete_limit" 305 android:title="@string/pref_title_mms_delete" ... /> 306 </PreferenceCategory> 307 ... 308</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<PreferenceScreen>} 元素创建的项目选中后,即会打开一个单独的列表来显示嵌套设置。 321</p> 322 323<p>例如:</p> 324 325<pre> 326<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 327 <!-- opens a subscreen of settings --> 328 <PreferenceScreen 329 android:key="button_voicemail_category_key" 330 android:title="@string/voicemail" 331 android:persistent="false"> 332 <ListPreference 333 android:key="button_voicemail_provider_key" 334 android:title="@string/voicemail_provider" ... /> 335 <!-- opens another nested subscreen --> 336 <PreferenceScreen 337 android:key="button_voicemail_setting_key" 338 android:title="@string/voicemail_settings" 339 android:persistent="false"> 340 ... 341 </PreferenceScreen> 342 <RingtonePreference 343 android:key="button_voicemail_ringtone_key" 344 android:title="@string/voicemail_ringtone_title" 345 android:ringtoneType="notification" ... /> 346 ... 347 </PreferenceScreen> 348 ... 349</PreferenceScreen> 350</pre> 351 352 353<h3 id="Intents">使用 Intent </h3> 354 355<p>在某些情况下,您可能需要首选项来打开不同的 Activity(而不是 Web 356浏览器等设置屏幕)或查看网页。要在用户选择首选项时调用 {@link 357android.content.Intent},请将{@code <intent>} 358元素添加为相应 {@code <Preference>} 元素的子元素。</p> 359 360<p>例如,您可以按如下方法使用首选项打开网页:</p> 361 362<pre> 363<Preference android:title="@string/prefs_web_page" > 364 <intent android:action="android.intent.action.VIEW" 365 android:data="http://www.example.com" /> 366</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 @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 @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 @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<!-- default value is a boolean --> 500<CheckBoxPreference 501 android:defaultValue="true" 502 ... /> 503 504<!-- default value is a string --> 505<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 <header>} 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 <preference-headers>} 元素内的单个 {@code <header>} 586元素指定。例如:</p> 587 588<pre> 589<?xml version="1.0" encoding="utf-8"?> 590<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> 591 <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 <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 <!-- key/value pairs can be included as arguments for the fragment. --> 600 <extra android:name="someKey" android:value="someHeaderValue" /> 601 </header> 602</preference-headers> 603</pre> 604 605<p>每个标头均可使用 {@code android:fragment} 属性声明在用户选择该标头时应打开的 {@link 606android.preference.PreferenceFragment} 实例。</p> 607 608<p>{@code <extras>} 元素允许您使用 {@link 609android.os.Bundle} 将键值对传递给片段。该片段可以通过调用 {@link 610android.app.Fragment#getArguments()} 检索参数。您向该片段传递参数的原因可能有很多,不过一个重要原因是,要对每个组重复使用 611{@link 612android.preference.PreferenceFragment} 613的相同子类,而且要使用参数来指定该片段应加载哪些首选项 XML 文件。</p> 614 615<p>例如,当每个标头均使用 616{@code "settings"} 键定义 {@code <extra>} 参数时,则可以对多个设置组重复使用以下片段:</p> 617 618<pre> 619public static class SettingsFragment extends PreferenceFragment { 620 @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 @Override 646 public void onBuildHeaders(List<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<Preference>} 元素的首选项 XML 文件即可,这些基本元素的行为方式与标头项目类似(供较旧版本的 Android 667 系统使用)。</p> 668 669<p>但是,每个 {@link 670android.preference.Preference <Preference>} 元素均会向 {@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<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> 679 <header 680 android:fragment="com.example.prefs.SettingsFragmentOne" 681 android:title="@string/prefs_category_one" 682 android:summary="@string/prefs_summ_category_one" /> 683 <header 684 android:fragment="com.example.prefs.SettingsFragmentTwo" 685 android:title="@string/prefs_category_two" 686 android:summary="@string/prefs_summ_category_two" /> 687</preference-headers> 688</pre> 689 690<p>下面是为早于 691Android 3.0 版本的系统提供相同标头的首选项文件 ({@code res/xml/preference_headers_legacy.xml}):</p> 692 693<pre> 694<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 695 <Preference 696 android:title="@string/prefs_category_one" 697 android:summary="@string/prefs_summ_category_one" > 698 <intent 699 android:targetPackage="com.example.prefs" 700 android:targetClass="com.example.prefs.SettingsActivity" 701 android:action="com.example.prefs.PREFS_ONE" /> 702 </Preference> 703 <Preference 704 android:title="@string/prefs_category_two" 705 android:summary="@string/prefs_summ_category_two" > 706 <intent 707 android:targetPackage="com.example.prefs" 708 android:targetClass="com.example.prefs.SettingsActivity" 709 android:action="com.example.prefs.PREFS_TWO" /> 710 </Preference> 711</PreferenceScreen> 712</pre> 713 714<p>由于是从 Android 3.0 开始方添加对 {@code <preference-headers>} 的支持,因此只有在 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@Override 725public void onCreate(Bundle savedInstanceState) { 726 super.onCreate(savedInstanceState); 727 ... 728 729 if (Build.VERSION.SDK_INT < 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@Override 737public void onBuildHeaders(List<Header> target) { 738 loadHeadersFromResource(R.xml.preference_headers, target); 739} 740</pre> 741 742<p>最后要做的就是处理传入 Activity 的 743{@link android.content.Intent},以确定要加载的首选项文件。因此,请检索 Intent 的操作,并将其与在首选项 XML 的 {@code <intent>} 744标记中使用的已知操作字符串进行比较。</p> 745 746<pre> 747final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE"; 748... 749 750@Override 751public void onCreate(Bundle savedInstanceState) { 752 super.onCreate(savedInstanceState); 753 754 String action = getIntent().getAction(); 755 if (action != null && action.equals(ACTION_PREFS_ONE)) { 756 addPreferencesFromResource(R.xml.preferences); 757 } 758 ... 759 760 else if (Build.VERSION.SDK_INT < 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@Override 846protected void onResume() { 847 super.onResume(); 848 getPreferenceScreen().getSharedPreferences() 849 .registerOnSharedPreferenceChangeListener(this); 850} 851 852@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<activity android:name="SettingsActivity" ... > 916 <intent-filter> 917 <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" /> 918 <category android:name="android.intent.category.DEFAULT" /> 919 </intent-filter> 920</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@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@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@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 @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<SavedState> CREATOR = 1144 new Parcelable.Creator<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@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@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