settings.jd revision 183bf116978e3c44292c9ead2bceb47e972624a1
1page.title=Settings 2@jd:body 3 4 5<div id="qv-wrapper"> 6<div id="qv"> 7 8<h2>In this document</h2> 9<ol> 10 <li><a href="#Overview">Overview</a> 11 <ol> 12 <li><a href="#SettingTypes">Preferences</a></li> 13 </ol> 14 </li> 15 <li><a href="#DefiningPrefs">Defining Preferences in XML</a> 16 <ol> 17 <li><a href="#Groups">Creating setting groups</a></li> 18 <li><a href="#Intents">Using intents</a></li> 19 </ol> 20 </li> 21 <li><a href="#Activity">Creating a Preference Activity</a></li> 22 <li><a href="#Fragment">Using Preference Fragments</a></li> 23 <li><a href="#Defaults">Setting Default Values</a></li> 24 <li><a href="#PreferenceHeaders">Using Preference Headers</a> 25 <ol> 26 <li><a href="#CreateHeaders">Creating the headers file</a></li> 27 <li><a href="#DisplayHeaders">Displaying the headers</a></li> 28 <li><a href="#BackCompatHeaders">Supporting older versions with preference headers</a></li> 29 </ol> 30 </li> 31 <li><a href="#ReadingPrefs">Reading Preferences</a> 32 <ol> 33 <li><a href="#Listening">Listening for preference changes</a></li> 34 </ol> 35 </li> 36 <li><a href="#NetworkUsage">Managing Network Usage</a></li> 37 <li><a href="#Custom">Building a Custom Preference</a> 38 <ol> 39 <li><a href="#CustomSelected">Specifying the user interface</a></li> 40 <li><a href="#CustomSave">Saving the setting's value</a></li> 41 <li><a href="#CustomInitialize">Initializing the current value</a></li> 42 <li><a href="#CustomDefault">Providing a default value</a></li> 43 <li><a href="#CustomSaveState">Saving and restoring the Preference's state</a></li> 44 </ol> 45 </li> 46</ol> 47 48<h2>Key classes</h2> 49<ol> 50 <li>{@link android.preference.Preference}</li> 51 <li>{@link android.preference.PreferenceActivity}</li> 52 <li>{@link android.preference.PreferenceFragment}</li> 53</ol> 54 55 56<h2>See also</h2> 57<ol> 58 <li><a 59href="{@docRoot}design/patterns/settings.html">Settings design guide</a></li> 60</ol> 61</div> 62</div> 63 64 65 66 67<p>Applications often include settings that allow users to modify app features and behaviors. For 68example, some apps allow users to specify whether notifications are enabled or specify how often the 69application syncs data with the cloud.</p> 70 71<p>If you want to provide settings for your app, you should use 72Android's {@link android.preference.Preference} APIs to build an interface that's consistent with 73the user experience in other Android apps (including the system settings). This document describes 74how to build your app settings using {@link android.preference.Preference} APIs.</p> 75 76<div class="note design"> 77<p><strong>Settings Design</strong></p> 78 <p>For information about how to design your settings, read the <a 79href="{@docRoot}design/patterns/settings.html">Settings</a> design guide.</p> 80</div> 81 82 83<img src="{@docRoot}images/ui/settings/settings.png" alt="" width="435" /> 84<p class="img-caption"><strong>Figure 1.</strong> Screenshots from the Android Messaging app's 85settings. Selecting an item defined by a {@link android.preference.Preference} 86opens an interface to change the setting.</p> 87 88 89 90 91<h2 id="Overview">Overview</h2> 92 93<p>Instead of using {@link android.view.View} objects to build the user interface, settings are 94built using various subclasses of the {@link android.preference.Preference} class that you 95declare in an XML file.</p> 96 97<p>A {@link android.preference.Preference} object is the building block for a single 98setting. Each {@link android.preference.Preference} appears as an item in a list and provides the 99appropriate UI for users to modify the setting. For example, a {@link 100android.preference.CheckBoxPreference} creates a list item that shows a checkbox, and a {@link 101android.preference.ListPreference} creates an item that opens a dialog with a list of choices.</p> 102 103<p>Each {@link android.preference.Preference} you add has a corresponding key-value pair that 104the system uses to save the setting in a default {@link android.content.SharedPreferences} 105file for your app's settings. When the user changes a setting, the system updates the corresponding 106value in the {@link android.content.SharedPreferences} file for you. The only time you should 107directly interact with the associated {@link android.content.SharedPreferences} file is when you 108need to read the value in order to determine your app's behavior based on the user's setting.</p> 109 110<p>The value saved in {@link android.content.SharedPreferences} for each setting can be one of the 111following data types:</p> 112 113<ul> 114 <li>Boolean</li> 115 <li>Float</li> 116 <li>Int</li> 117 <li>Long</li> 118 <li>String</li> 119 <li>String {@link java.util.Set}</li> 120</ul> 121 122<p>Because your app's settings UI is built using {@link android.preference.Preference} objects 123instead of 124{@link android.view.View} objects, you need to use a specialized {@link android.app.Activity} or 125{@link android.app.Fragment} subclass to display the list settings:</p> 126 127<ul> 128 <li>If your app supports versions of Android older than 3.0 (API level 10 and lower), you must 129build the activity as an extension of the {@link android.preference.PreferenceActivity} class.</li> 130 <li>On Android 3.0 and later, you should instead use a traditional {@link android.app.Activity} 131that hosts a {@link android.preference.PreferenceFragment} that displays your app settings. 132However, you can also use {@link android.preference.PreferenceActivity} to create a two-pane layout 133for large screens when you have multiple groups of settings.</li> 134</ul> 135 136<p>How to set up your {@link android.preference.PreferenceActivity} and instances of {@link 137android.preference.PreferenceFragment} is discussed in the sections about <a 138href="#Activity">Creating a Preference Activity</a> and <a href="#Fragment">Using 139Preference Fragments</a>.</p> 140 141 142<h3 id="SettingTypes">Preferences</h3> 143 144<p>Every setting for your app is represented by a specific subclass of the {@link 145android.preference.Preference} class. Each subclass includes a set of core properties that allow you 146to specify things such as a title for the setting and the default value. Each subclass also provides 147its own specialized properties and user interface. For instance, figure 1 shows a screenshot from 148the Messaging app's settings. Each list item in the settings screen is backed by a different {@link 149android.preference.Preference} object.</p> 150 151<p>A few of the most common preferences are:</p> 152 153<dl> 154 <dt>{@link android.preference.CheckBoxPreference}</dt> 155 <dd>Shows an item with a checkbox for a setting that is either enabled or disabled. The saved 156value is a boolean (<code>true</code> if it's checked).</dd> 157 158 <dt>{@link android.preference.ListPreference}</dt> 159 <dd>Opens a dialog with a list of radio buttons. The saved value 160can be any one of the supported value types (listed above).</dd> 161 162 <dt>{@link android.preference.EditTextPreference}</dt> 163 <dd>Opens a dialog with an {@link android.widget.EditText} widget. The saved value is a {@link 164java.lang.String}.</dd> 165</dl> 166 167<p>See the {@link android.preference.Preference} class for a list of all other subclasses and their 168corresponding properties.</p> 169 170<p>Of course, the built-in classes don't accommodate every need and your application might require 171something more specialized. For example, the platform currently does not provide a {@link 172android.preference.Preference} class for picking a number or a date. So you might need to define 173your own {@link android.preference.Preference} subclass. For help doing so, see the section about <a 174href="#Custom">Building a Custom Preference</a>.</p> 175 176 177 178<h2 id="DefiningPrefs">Defining Preferences in XML</h2> 179 180<p>Although you can instantiate new {@link android.preference.Preference} objects at runtime, you 181should define your list of settings in XML with a hierarchy of {@link android.preference.Preference} 182objects. Using an XML file to define your collection of settings is preferred because the file 183provides an easy-to-read structure that's simple to update. Also, your app's settings are 184generally pre-determined, although you can still modify the collection at runtime.</p> 185 186<p>Each {@link android.preference.Preference} subclass can be declared with an XML element that 187matches the class name, such as {@code <CheckBoxPreference>}.</p> 188 189<p>You must save the XML file in the {@code res/xml/} directory. Although you can name the file 190anything you want, it's traditionally named {@code preferences.xml}. You usually need only one file, 191because branches in the hierarchy (that open their own list of settings) are declared using nested 192instances of {@link android.preference.PreferenceScreen}.</p> 193 194<p class="note"><strong>Note:</strong> If you want to create a multi-pane layout for your 195settings, then you need separate XML files for each fragment.</p> 196 197<p>The root node for the XML file must be a {@link android.preference.PreferenceScreen 198<PreferenceScreen>} element. Within this element is where you add each {@link 199android.preference.Preference}. Each child you add within the 200{@link android.preference.PreferenceScreen <PreferenceScreen>} element appears as a single 201item in the list of settings.</p> 202 203<p>For example:</p> 204 205<pre> 206<?xml version="1.0" encoding="utf-8"?> 207<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 208 <CheckBoxPreference 209 android:key="pref_sync" 210 android:title="@string/pref_sync" 211 android:summary="@string/pref_sync_summ" 212 android:defaultValue="true" /> 213 <ListPreference 214 android:dependency="pref_sync" 215 android:key="pref_syncConnectionType" 216 android:title="@string/pref_syncConnectionType" 217 android:dialogTitle="@string/pref_syncConnectionType" 218 android:entries="@array/pref_syncConnectionTypes_entries" 219 android:entryValues="@array/pref_syncConnectionTypes_values" 220 android:defaultValue="@string/pref_syncConnectionTypes_default" /> 221</PreferenceScreen> 222</pre> 223 224<p>In this example, there's a {@link android.preference.CheckBoxPreference} and a {@link 225android.preference.ListPreference}. Both items include the following three attributes:</p> 226 227<dl> 228 <dt>{@code android:key}</dt> 229 <dd>This attribute is required for preferences that persist a data value. It specifies the unique 230key (a string) the system uses when saving this setting's value in the {@link 231android.content.SharedPreferences}. 232 <p>The only instances in which this attribute is <em>not required</em> is when the preference is a 233{@link android.preference.PreferenceCategory} or {@link android.preference.PreferenceScreen}, or the 234preference specifies an {@link android.content.Intent} to invoke (with an <a 235href="#Intents">{@code <intent>}</a> element) or a {@link android.app.Fragment} to display (with an <a 236href="{@docRoot}reference/android/preference/Preference.html#attr_android:fragment">{@code 237android:fragment}</a> attribute).</p> 238 </dd> 239 <dt>{@code android:title}</dt> 240 <dd>This provides a user-visible name for the setting.</dd> 241 <dt>{@code android:defaultValue}</dt> 242 <dd>This specifies the initial value that the system should set in the {@link 243android.content.SharedPreferences} file. You should supply a default value for all 244settings.</dd> 245</dl> 246 247<p>For information about all other supported attributes, see the {@link 248android.preference.Preference} (and respective subclass) documentation.</p> 249 250 251<div class="figure" style="width:300px"> 252 <img src="{@docRoot}images/ui/settings/settings-titles.png" alt="" /> 253 <p class="img-caption"><strong>Figure 2.</strong> Setting categories 254 with titles. <br/><b>1.</b> The category is specified by the {@link 255android.preference.PreferenceCategory <PreferenceCategory>} element. <br/><b>2.</b> The title is 256specified with the {@code android:title} attribute.</p> 257</div> 258 259 260<p>When your list of settings exceeds about 10 items, you might want to add titles to 261define groups of settings or display those groups in a 262separate screen. These options are described in the following sections.</p> 263 264 265<h3 id="Groups">Creating setting groups</h3> 266 267<p>If you present a list of 10 or more settings, users 268may have difficulty scanning, comprehending, and processing them. You can remedy this by 269dividing some or all of the settings into groups, effectively turning one long list into multiple 270shorter lists. A group of related settings can be presented in one of two ways:</p> 271 272<ul> 273 <li><a href="#Titles">Using titles</a></li> 274 <li><a href="#Subscreens">Using subscreens</a></li> 275</ul> 276 277<p>You can use one or both of these grouping techniques to organize your app's settings. When 278deciding which to use and how to divide your settings, you should follow the guidelines in Android 279Design's <a href="{@docRoot}design/patterns/settings.html">Settings</a> guide.</p> 280 281 282<h4 id="Titles">Using titles</h4> 283 284<p>If you want to provide dividers with headings between groups of settings (as shown in figure 2), 285place each group of {@link android.preference.Preference} objects inside a {@link 286android.preference.PreferenceCategory}.</p> 287 288<p>For example:</p> 289 290<pre> 291<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 292 <PreferenceCategory 293 android:title="@string/pref_sms_storage_title" 294 android:key="pref_key_storage_settings"> 295 <CheckBoxPreference 296 android:key="pref_key_auto_delete" 297 android:summary="@string/pref_summary_auto_delete" 298 android:title="@string/pref_title_auto_delete" 299 android:defaultValue="false"... /> 300 <Preference 301 android:key="pref_key_sms_delete_limit" 302 android:dependency="pref_key_auto_delete" 303 android:summary="@string/pref_summary_delete_limit" 304 android:title="@string/pref_title_sms_delete"... /> 305 <Preference 306 android:key="pref_key_mms_delete_limit" 307 android:dependency="pref_key_auto_delete" 308 android:summary="@string/pref_summary_delete_limit" 309 android:title="@string/pref_title_mms_delete" ... /> 310 </PreferenceCategory> 311 ... 312</PreferenceScreen> 313</pre> 314 315 316<h4 id="Subscreens">Using subscreens</h4> 317 318<p>If you want to place groups of settings into a subscreen (as shown in figure 3), place the group 319of {@link android.preference.Preference} objects inside a {@link 320android.preference.PreferenceScreen}.</p> 321 322<img src="{@docRoot}images/ui/settings/settings-subscreen.png" alt="" /> 323<p class="img-caption"><strong>Figure 3.</strong> Setting subscreens. The {@code 324<PreferenceScreen>} element 325creates an item that, when selected, opens a separate list to display the nested settings.</p> 326 327<p>For example:</p> 328 329<pre> 330<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 331 <!-- opens a subscreen of settings --> 332 <PreferenceScreen 333 android:key="button_voicemail_category_key" 334 android:title="@string/voicemail" 335 android:persistent="false"> 336 <ListPreference 337 android:key="button_voicemail_provider_key" 338 android:title="@string/voicemail_provider" ... /> 339 <!-- opens another nested subscreen --> 340 <PreferenceScreen 341 android:key="button_voicemail_setting_key" 342 android:title="@string/voicemail_settings" 343 android:persistent="false"> 344 ... 345 </PreferenceScreen> 346 <RingtonePreference 347 android:key="button_voicemail_ringtone_key" 348 android:title="@string/voicemail_ringtone_title" 349 android:ringtoneType="notification" ... /> 350 ... 351 </PreferenceScreen> 352 ... 353</PreferenceScreen> 354</pre> 355 356 357<h3 id="Intents">Using intents</h3> 358 359<p>In some cases, you might want a preference item to open a different activity instead of a 360settings screen, such as a web browser to view a web page. To invoke an {@link 361android.content.Intent} when the user selects a preference item, add an {@code <intent>} 362element as a child of the corresponding {@code <Preference>} element.</p> 363 364<p>For example, here's how you can use a preference item to open a web page:</p> 365 366<pre> 367<Preference android:title="@string/prefs_web_page" > 368 <intent android:action="android.intent.action.VIEW" 369 android:data="http://www.example.com" /> 370</Preference> 371</pre> 372 373<p>You can create both implicit and explicit intents using the following attributes:</p> 374 375<dl> 376 <dt>{@code android:action}</dt> 377 <dd>The action to assign, as per the {@link android.content.Intent#setAction setAction()} 378method.</dd> 379 <dt>{@code android:data}</dt> 380 <dd>The data to assign, as per the {@link android.content.Intent#setData setData()} method.</dd> 381 <dt>{@code android:mimeType}</dt> 382 <dd>The MIME type to assign, as per the {@link android.content.Intent#setType setType()} 383method.</dd> 384 <dt>{@code android:targetClass}</dt> 385 <dd>The class part of the component name, as per the {@link android.content.Intent#setComponent 386setComponent()} method.</dd> 387 <dt>{@code android:targetPackage}</dt> 388 <dd>The package part of the component name, as per the {@link 389android.content.Intent#setComponent setComponent()} method.</dd> 390</dl> 391 392 393 394<h2 id="Activity">Creating a Preference Activity</h2> 395 396<p>To display your settings in an activity, extend the {@link 397android.preference.PreferenceActivity} class. This is an extension of the traditional {@link 398android.app.Activity} class that displays a list of settings based on a hierarchy of {@link 399android.preference.Preference} objects. The {@link android.preference.PreferenceActivity} 400automatically persists the settings associated with each {@link 401android.preference.Preference} when the user makes a change.</p> 402 403<p class="note"><strong>Note:</strong> If you're developing your application for Android 3.0 and 404higher, you should instead use {@link android.preference.PreferenceFragment}. Go to the next 405section about <a href="#Fragment">Using Preference Fragments</a>.</p> 406 407<p>The most important thing to remember is that you do not load a layout of views during the {@link 408android.preference.PreferenceActivity#onCreate onCreate()} callback. Instead, you call {@link 409android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} to 410add the preferences you've declared in an XML file to the activity. For example, here's the bare 411minimum code required for a functional {@link android.preference.PreferenceActivity}:</p> 412 413<pre> 414public class SettingsActivity extends PreferenceActivity { 415 @Override 416 public void onCreate(Bundle savedInstanceState) { 417 super.onCreate(savedInstanceState); 418 addPreferencesFromResource(R.xml.preferences); 419 } 420} 421</pre> 422 423<p>This is actually enough code for some apps, because as soon as the user modifies a preference, 424the system saves the changes to a default {@link android.content.SharedPreferences} file that your 425other application components can read when you need to check the user's settings. Many apps, 426however, require a little more code in order to listen for changes that occur to the preferences. 427For information about listening to changes in the {@link android.content.SharedPreferences} file, 428see the section about <a href="#ReadingPrefs">Reading Preferences</a>.</p> 429 430 431 432 433<h2 id="Fragment">Using Preference Fragments</h2> 434 435<p>If you're developing for Android 3.0 (API level 11) and higher, you should use a {@link 436android.preference.PreferenceFragment} to display your list of {@link android.preference.Preference} 437objects. You can add a {@link android.preference.PreferenceFragment} to any activity—you don't 438need to use {@link android.preference.PreferenceActivity}.</p> 439 440<p><a href="{@docRoot}guide/components/fragments.html">Fragments</a> provide a more 441flexible architecture for your application, compared to using activities alone, no matter what kind 442of activity you're building. As such, we suggest you use {@link 443android.preference.PreferenceFragment} to control the display of your settings instead of {@link 444android.preference.PreferenceActivity} when possible.</p> 445 446<p>Your implementation of {@link android.preference.PreferenceFragment} can be as simple as 447defining the {@link android.preference.PreferenceFragment#onCreate onCreate()} method to load a 448preferences file with {@link android.preference.PreferenceFragment#addPreferencesFromResource 449addPreferencesFromResource()}. For example:</p> 450 451<pre> 452public static class SettingsFragment extends PreferenceFragment { 453 @Override 454 public void onCreate(Bundle savedInstanceState) { 455 super.onCreate(savedInstanceState); 456 457 // Load the preferences from an XML resource 458 addPreferencesFromResource(R.xml.preferences); 459 } 460 ... 461} 462</pre> 463 464<p>You can then add this fragment to an {@link android.app.Activity} just as you would for any other 465{@link android.app.Fragment}. For example:</p> 466 467<pre> 468public class SettingsActivity extends Activity { 469 @Override 470 protected void onCreate(Bundle savedInstanceState) { 471 super.onCreate(savedInstanceState); 472 473 // Display the fragment as the main content. 474 getFragmentManager().beginTransaction() 475 .replace(android.R.id.content, new SettingsFragment()) 476 .commit(); 477 } 478} 479</pre> 480 481<p class="note"><strong>Note:</strong> A {@link android.preference.PreferenceFragment} doesn't have 482a its own {@link android.content.Context} object. If you need a {@link android.content.Context} 483object, you can call {@link android.app.Fragment#getActivity()}. However, be careful to call 484{@link android.app.Fragment#getActivity()} only when the fragment is attached to an activity. When 485the fragment is not yet attached, or was detached during the end of its lifecycle, {@link 486android.app.Fragment#getActivity()} will return null.</p> 487 488 489<h2 id="Defaults">Setting Default Values</h2> 490 491<p>The preferences you create probably define some important behaviors for your application, so it's 492necessary that you initialize the associated {@link android.content.SharedPreferences} file with 493default values for each {@link android.preference.Preference} when the user first opens your 494application.</p> 495 496<p>The first thing you must do is specify a default value for each {@link 497android.preference.Preference} 498object in your XML file using the {@code android:defaultValue} attribute. The value can be any data 499type that is appropriate for the corresponding {@link android.preference.Preference} object. For 500example:</p> 501 502<pre> 503<!-- default value is a boolean --> 504<CheckBoxPreference 505 android:defaultValue="true" 506 ... /> 507 508<!-- default value is a string --> 509<ListPreference 510 android:defaultValue="@string/pref_syncConnectionTypes_default" 511 ... /> 512</pre> 513 514<p>Then, from the {@link android.app.Activity#onCreate onCreate()} method in your application's main 515activity—and in any other activity through which the user may enter your application for the 516first time—call {@link android.preference.PreferenceManager#setDefaultValues 517setDefaultValues()}:</p> 518 519<pre> 520PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false); 521</pre> 522 523<p>Calling this during {@link android.app.Activity#onCreate onCreate()} ensures that your 524application is properly initialized with default settings, which your application might need to 525read in order to determine some behaviors (such as whether to download data while on a 526cellular network).</p> 527 528<p>This method takes three arguments:</p> 529<ul> 530 <li>Your application {@link android.content.Context}.</li> 531 <li>The resource ID for the preference XML file for which you want to set the default values.</li> 532 <li>A boolean indicating whether the default values should be set more than once. 533<p>When <code>false</code>, the system sets the default values only if this method has never been 534called in the past (or the {@link android.preference.PreferenceManager#KEY_HAS_SET_DEFAULT_VALUES} 535in the default value shared preferences file is false).</p></li> 536</ul> 537 538<p>As long as you set the third argument to <code>false</code>, you can safely call this method 539every time your activity starts without overriding the user's saved preferences by resetting them to 540the defaults. However, if you set it to <code>true</code>, you will override any previous 541values with the defaults.</p> 542 543 544 545<h2 id="PreferenceHeaders">Using Preference Headers</h2> 546 547<p>In rare cases, you might want to design your settings such that the first screen 548displays only a list of <a href="#Subscreens">subscreens</a> (such as in the system Settings app, 549as shown in figures 4 and 5). When you're developing such a design for Android 3.0 and higher, you 550should use a new "headers" feature in Android 3.0, instead of building subscreens with nested 551{@link android.preference.PreferenceScreen} elements.</p> 552 553<p>To build your settings with headers, you need to:</p> 554<ol> 555 <li>Separate each group of settings into separate instances of {@link 556android.preference.PreferenceFragment}. That is, each group of settings needs a separate XML 557file.</li> 558 <li>Create an XML headers file that lists each settings group and declares which fragment 559contains the corresponding list of settings.</li> 560 <li>Extend the {@link android.preference.PreferenceActivity} class to host your settings.</li> 561 <li>Implement the {@link 562android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} callback to specify the 563headers file.</li> 564</ol> 565 566<p>A great benefit to using this design is that {@link android.preference.PreferenceActivity} 567automatically presents the two-pane layout shown in figure 4 when running on large screens.</p> 568 569<p>Even if your application supports versions of Android older than 3.0, you can build your 570application to use {@link android.preference.PreferenceFragment} for a two-pane presentation on 571newer devices while still supporting a traditional multi-screen hierarchy on older 572devices (see the section about <a href="#BackCompatHeaders">Supporting older versions with 573preference headers</a>).</p> 574 575<img src="{@docRoot}images/ui/settings/settings-headers-tablet.png" alt="" /> 576<p class="img-caption"><strong>Figure 4.</strong> Two-pane layout with headers. <br/><b>1.</b> The 577headers are defined with an XML headers file. <br/><b>2.</b> Each group of settings is defined by a 578{@link android.preference.PreferenceFragment} that's specified by a {@code <header>} element in 579the headers file.</p> 580 581<img src="{@docRoot}images/ui/settings/settings-headers-handset.png" alt="" /> 582<p class="img-caption"><strong>Figure 5.</strong> A handset device with setting headers. When an 583item is selected, the associated {@link android.preference.PreferenceFragment} replaces the 584headers.</p> 585 586 587<h3 id="CreateHeaders" style="clear:left">Creating the headers file</h3> 588 589<p>Each group of settings in your list of headers is specified by a single {@code <header>} 590element inside a root {@code <preference-headers>} element. For example:</p> 591 592<pre> 593<?xml version="1.0" encoding="utf-8"?> 594<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> 595 <header 596 android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne" 597 android:title="@string/prefs_category_one" 598 android:summary="@string/prefs_summ_category_one" /> 599 <header 600 android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo" 601 android:title="@string/prefs_category_two" 602 android:summary="@string/prefs_summ_category_two" > 603 <!-- key/value pairs can be included as arguments for the fragment. --> 604 <extra android:name="someKey" android:value="someHeaderValue" /> 605 </header> 606</preference-headers> 607</pre> 608 609<p>With the {@code android:fragment} attribute, each header declares an instance of {@link 610android.preference.PreferenceFragment} that should open when the user selects the header.</p> 611 612<p>The {@code <extras>} element allows you to pass key-value pairs to the fragment in a {@link 613android.os.Bundle}. The fragment can retrieve the arguments by calling {@link 614android.app.Fragment#getArguments()}. You might pass arguments to the fragment for a variety of 615reasons, but one good reason is to reuse the same subclass of {@link 616android.preference.PreferenceFragment} for each group and use the argument to specify which 617preferences XML file the fragment should load.</p> 618 619<p>For example, here's a fragment that can be reused for multiple settings groups, when each 620header defines an {@code <extra>} argument with the {@code "settings"} key:</p> 621 622<pre> 623public static class SettingsFragment extends PreferenceFragment { 624 @Override 625 public void onCreate(Bundle savedInstanceState) { 626 super.onCreate(savedInstanceState); 627 628 String settings = getArguments().getString("settings"); 629 if ("notifications".equals(settings)) { 630 addPreferencesFromResource(R.xml.settings_wifi); 631 } else if ("sync".equals(settings)) { 632 addPreferencesFromResource(R.xml.settings_sync); 633 } 634 } 635} 636</pre> 637 638 639 640<h3 id="DisplayHeaders">Displaying the headers</h3> 641 642<p>To display the preference headers, you must implement the {@link 643android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} callback method and call 644{@link android.preference.PreferenceActivity#loadHeadersFromResource 645loadHeadersFromResource()}. For example:</p> 646 647<pre> 648public class SettingsActivity extends PreferenceActivity { 649 @Override 650 public void onBuildHeaders(List<Header> target) { 651 loadHeadersFromResource(R.xml.preference_headers, target); 652 } 653} 654</pre> 655 656<p>When the user selects an item from the list of headers, the system opens the associated {@link 657android.preference.PreferenceFragment}.</p> 658 659<p class="note"><strong>Note:</strong> When using preference headers, your subclass of {@link 660android.preference.PreferenceActivity} doesn't need to implement the {@link 661android.preference.PreferenceActivity#onCreate onCreate()} method, because the only required 662task for the activity is to load the headers.</p> 663 664 665<h3 id="BackCompatHeaders">Supporting older versions with preference headers</h3> 666 667<p>If your application supports versions of Android older than 3.0, you can still use headers to 668provide a two-pane layout when running on Android 3.0 and higher. All you need to do is create an 669additional preferences XML file that uses basic {@link android.preference.Preference 670<Preference>} elements that behave like the header items (to be used by the older Android 671versions).</p> 672 673<p>Instead of opening a new {@link android.preference.PreferenceScreen}, however, each of the {@link 674android.preference.Preference <Preference>} elements sends an {@link android.content.Intent} to 675the {@link android.preference.PreferenceActivity} that specifies which preference XML file to 676load.</p> 677 678<p>For example, here's an XML file for preference headers that is used on Android 3.0 679and higher ({@code res/xml/preference_headers.xml}):</p> 680 681<pre> 682<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> 683 <header 684 android:fragment="com.example.prefs.SettingsFragmentOne" 685 android:title="@string/prefs_category_one" 686 android:summary="@string/prefs_summ_category_one" /> 687 <header 688 android:fragment="com.example.prefs.SettingsFragmentTwo" 689 android:title="@string/prefs_category_two" 690 android:summary="@string/prefs_summ_category_two" /> 691</preference-headers> 692</pre> 693 694<p>And here is a preference file that provides the same headers for versions older than 695Android 3.0 ({@code res/xml/preference_headers_legacy.xml}):</p> 696 697<pre> 698<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 699 <Preference 700 android:title="@string/prefs_category_one" 701 android:summary="@string/prefs_summ_category_one" > 702 <intent 703 android:targetPackage="com.example.prefs" 704 android:targetClass="com.example.prefs.SettingsActivity" 705 android:action="com.example.prefs.PREFS_ONE" /> 706 </Preference> 707 <Preference 708 android:title="@string/prefs_category_two" 709 android:summary="@string/prefs_summ_category_two" > 710 <intent 711 android:targetPackage="com.example.prefs" 712 android:targetClass="com.example.prefs.SettingsActivity" 713 android:action="com.example.prefs.PREFS_TWO" /> 714 </Preference> 715</PreferenceScreen> 716</pre> 717 718<p>Because support for {@code <preference-headers>} was added in Android 3.0, the system calls 719{@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} in your {@link 720android.preference.PreferenceActivity} only when running on Androd 3.0 or higher. In order to load 721the "legacy" headers file ({@code preference_headers_legacy.xml}), you must check the Android 722version and, if the version is older than Android 3.0 ({@link 723android.os.Build.VERSION_CODES#HONEYCOMB}), call {@link 724android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} to 725load the legacy header file. For example:</p> 726 727<pre> 728@Override 729public void onCreate(Bundle savedInstanceState) { 730 super.onCreate(savedInstanceState); 731 ... 732 733 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 734 // Load the legacy preferences headers 735 addPreferencesFromResource(R.xml.preference_headers_legacy); 736 } 737} 738 739// Called only on Honeycomb and later 740@Override 741public void onBuildHeaders(List<Header> target) { 742 loadHeadersFromResource(R.xml.preference_headers, target); 743} 744</pre> 745 746<p>The only thing left to do is handle the {@link android.content.Intent} that's passed into the 747activity to identify which preference file to load. So retrieve the intent's action and compare it 748to known action strings that you've used in the preference XML's {@code <intent>} tags:</p> 749 750<pre> 751final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE"; 752... 753 754@Override 755public void onCreate(Bundle savedInstanceState) { 756 super.onCreate(savedInstanceState); 757 758 String action = getIntent().getAction(); 759 if (action != null && action.equals(ACTION_PREFS_ONE)) { 760 addPreferencesFromResource(R.xml.preferences); 761 } 762 ... 763 764 else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 765 // Load the legacy preferences headers 766 addPreferencesFromResource(R.xml.preference_headers_legacy); 767 } 768} 769</pre> 770 771<p>Beware that consecutive calls to {@link 772android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} will 773stack all the preferences in a single list, so be sure that it's only called once by chaining the 774conditions with else-if statements.</p> 775 776 777 778 779 780<h2 id="ReadingPrefs">Reading Preferences</h2> 781 782<p>By default, all your app's preferences are saved to a file that's accessible from anywhere 783within your application by calling the static method {@link 784android.preference.PreferenceManager#getDefaultSharedPreferences 785PreferenceManager.getDefaultSharedPreferences()}. This returns the {@link 786android.content.SharedPreferences} object containing all the key-value pairs that are associated 787with the {@link android.preference.Preference} objects used in your {@link 788android.preference.PreferenceActivity}.</p> 789 790<p>For example, here's how you can read one of the preference values from any other activity in your 791application:</p> 792 793<pre> 794SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); 795String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, ""); 796</pre> 797 798 799 800<h3 id="Listening">Listening for preference changes</h3> 801 802<p>There are several reasons you might want to be notified as soon as the use changes one of the 803preferences. In order to receive a callback when a change happens to any one of the preferences, 804implement the {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener 805SharedPreference.OnSharedPreferenceChangeListener} interface and register the listener for the 806{@link android.content.SharedPreferences} object by calling {@link 807android.content.SharedPreferences#registerOnSharedPreferenceChangeListener 808registerOnSharedPreferenceChangeListener()}.</p> 809 810<p>The interface has only one callback method, {@link 811android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged 812onSharedPreferenceChanged()}, and you might find it easiest to implement the interface as a part of 813your activity. For example:</p> 814 815<pre> 816public class SettingsActivity extends PreferenceActivity 817 implements OnSharedPreferenceChangeListener { 818 public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType"; 819 ... 820 821 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 822 if (key.equals(KEY_PREF_SYNC_CONN)) { 823 Preference connectionPref = findPreference(key); 824 // Set summary to be the user-description for the selected value 825 connectionPref.setSummary(sharedPreferences.getString(key, "")); 826 } 827 } 828} 829</pre> 830 831<p>In this example, the method checks whether the changed setting is for a known preference key. It 832calls {@link android.preference.PreferenceActivity#findPreference findPreference()} to get the 833{@link android.preference.Preference} object that was changed so it can modify the item's 834summary to be a description of the user's selection. That is, when the setting is a {@link 835android.preference.ListPreference} or other multiple choice setting, you should call {@link 836android.preference.Preference#setSummary setSummary()} when the setting changes to display the 837current status (such as the Sleep setting shown in figure 5).</p> 838 839<p class="note"><strong>Note:</strong> As described in the Android Design document about <a 840href="{@docRoot}design/patterns/settings.html">Settings</a>, we recommend that you update the 841summary for a {@link android.preference.ListPreference} each time the user changes the preference in 842order to describe the current setting.</p> 843 844<p>For proper lifecycle management in the activity, we recommend that you register and unregister 845your {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener} during the {@link 846android.app.Activity#onResume} and {@link android.app.Activity#onPause} callbacks, respectively:</p> 847 848<pre> 849@Override 850protected void onResume() { 851 super.onResume(); 852 getPreferenceScreen().getSharedPreferences() 853 .registerOnSharedPreferenceChangeListener(this); 854} 855 856@Override 857protected void onPause() { 858 super.onPause(); 859 getPreferenceScreen().getSharedPreferences() 860 .unregisterOnSharedPreferenceChangeListener(this); 861} 862</pre> 863 864 865 866<h2 id="NetworkUsage">Managing Network Usage</h2> 867 868 869<p>Beginning with Android 4.0, the system's Settings application allows users to see how much 870network data their applications are using while in the foreground and background. Users can then 871disable the use of background data for individual apps. In order to avoid users disabling your app's 872access to data from the background, you should use the data connection efficiently and allow 873users to refine your app's data usage through your application settings.<p> 874 875<p>For example, you might allow the user to control how often your app syncs data, whether your app 876performs uploads/downloads only when on Wi-Fi, whether your app uses data while roaming, etc. With 877these controls available to them, users are much less likely to disable your app's access to data 878when they approach the limits they set in the system Settings, because they can instead precisely 879control how much data your app uses.</p> 880 881<p>Once you've added the necessary preferences in your {@link android.preference.PreferenceActivity} 882to control your app's data habits, you should add an intent filter for {@link 883android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} in your manifest file. For example:</p> 884 885<pre> 886<activity android:name="SettingsActivity" ... > 887 <intent-filter> 888 <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" /> 889 <category android:name="android.intent.category.DEFAULT" /> 890 </intent-filter> 891</activity> 892</pre> 893 894<p>This intent filter indicates to the system that this is the activity that controls your 895application's data usage. Thus, when the user inspects how much data your app is using from the 896system's Settings app, a <em>View application settings</em> button is available that launches your 897{@link android.preference.PreferenceActivity} so the user can refine how much data your app 898uses.</p> 899 900 901 902 903 904 905 906<h2 id="Custom">Building a Custom Preference</h2> 907 908<p>The Android framework includes a variety of {@link android.preference.Preference} subclasses that 909allow you to build a UI for several different types of settings. 910However, you might discover a setting you need for which there’s no built-in solution, such as a 911number picker or date picker. In such a case, you’ll need to create a custom preference by extending 912the {@link android.preference.Preference} class or one of the other subclasses.</p> 913 914<p>When you extend the {@link android.preference.Preference} class, there are a few important 915things you need to do:</p> 916 917<ul> 918 <li>Specify the user interface that appears when the user selects the settings.</li> 919 <li>Save the setting's value when appropriate.</li> 920 <li>Initialize the {@link android.preference.Preference} with the current (or default) value 921when it comes into view.</li> 922 <li>Provide the default value when requested by the system.</li> 923 <li>If the {@link android.preference.Preference} provides its own UI (such as a dialog), save 924and restore the state to handle lifecycle changes (such as when the user rotates the screen).</li> 925</ul> 926 927<p>The following sections describe how to accomplish each of these tasks.</p> 928 929 930 931<h3 id="CustomSelected">Specifying the user interface</h3> 932 933 <p>If you directly extend the {@link android.preference.Preference} class, you need to implement 934{@link android.preference.Preference#onClick()} to define the action that occurs when the user 935selects the item. However, most custom settings extend {@link android.preference.DialogPreference} to 936show a dialog, which simplifies the procedure. When you extend {@link 937android.preference.DialogPreference}, you must call {@link 938android.preference.DialogPreference#setDialogLayoutResource setDialogLayoutResourcs()} during in the 939class constructor to specify the layout for the dialog.</p> 940 941 <p>For example, here's the constructor for a custom {@link 942android.preference.DialogPreference} that declares the layout and specifies the text for the 943default positive and negative dialog buttons:</p> 944 945<pre> 946public class NumberPickerPreference extends DialogPreference { 947 public NumberPickerPreference(Context context, AttributeSet attrs) { 948 super(context, attrs); 949 950 setDialogLayoutResource(R.layout.numberpicker_dialog); 951 setPositiveButtonText(android.R.string.ok); 952 setNegativeButtonText(android.R.string.cancel); 953 954 setDialogIcon(null); 955 } 956 ... 957} 958</pre> 959 960 961 962<h3 id="CustomSave">Saving the setting's value</h3> 963 964<p>You can save a value for the setting at any time by calling one of the {@link 965android.preference.Preference} class's {@code persist*()} methods, such as {@link 966android.preference.Preference#persistInt persistInt()} if the setting's value is an integer or 967{@link android.preference.Preference#persistBoolean persistBoolean()} to save a boolean.</p> 968 969<p class="note"><strong>Note:</strong> Each {@link android.preference.Preference} can save only one 970data type, so you must use the {@code persist*()} method appropriate for the data type used by your 971custom {@link android.preference.Preference}.</p> 972 973<p>When you choose to persist the setting can depend on which {@link 974android.preference.Preference} class you extend. If you extend {@link 975android.preference.DialogPreference}, then you should persist the value only when the dialog 976closes due to a positive result (the user selects the "OK" button).</p> 977 978<p>When a {@link android.preference.DialogPreference} closes, the system calls the {@link 979android.preference.DialogPreference#onDialogClosed onDialogClosed()} method. The method includes a 980boolean argument that specifies whether the user result is "positive"—if the value is 981<code>true</code>, then the user selected the positive button and you should save the new value. For 982example:</p> 983 984<pre> 985@Override 986protected void onDialogClosed(boolean positiveResult) { 987 // When the user selects "OK", persist the new value 988 if (positiveResult) { 989 persistInt(mNewValue); 990 } 991} 992</pre> 993 994<p>In this example, <code>mNewValue</code> is a class member that holds the setting's current 995value. Calling {@link android.preference.Preference#persistInt persistInt()} saves the value to 996the {@link android.content.SharedPreferences} file (automatically using the key that's 997specified in the XML file for this {@link android.preference.Preference}).</p> 998 999 1000<h3 id="CustomInitialize">Initializing the current value</h3> 1001 1002<p>When the system adds your {@link android.preference.Preference} to the screen, it 1003calls {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} to notify 1004you whether the setting has a persisted value. If there is no persisted value, this call provides 1005you the default value.</p> 1006 1007<p>The {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} method passes 1008a boolean, <code>restorePersistedValue</code>, to indicate whether a value has already been persisted 1009for the setting. If it is <code>true</code>, then you should retrieve the persisted value by calling 1010one of the {@link 1011android.preference.Preference} class's {@code getPersisted*()} methods, such as {@link 1012android.preference.Preference#getPersistedInt getPersistedInt()} for an integer value. You'll 1013usually want to retrieve the persisted value so you can properly update the UI to reflect the 1014previously saved value.</p> 1015 1016<p>If <code>restorePersistedValue</code> is <code>false</code>, then you 1017should use the default value that is passed in the second argument.</p> 1018 1019<pre> 1020@Override 1021protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { 1022 if (restorePersistedValue) { 1023 // Restore existing state 1024 mCurrentValue = this.getPersistedInt(DEFAULT_VALUE); 1025 } else { 1026 // Set default state from the XML attribute 1027 mCurrentValue = (Integer) defaultValue; 1028 persistInt(mCurrentValue); 1029 } 1030} 1031</pre> 1032 1033<p>Each {@code getPersisted*()} method takes an argument that specifies the 1034default value to use in case there is actually no persisted value or the key does not exist. In 1035the example above, a local constant is used to specify the default value in case {@link 1036android.preference.Preference#getPersistedInt getPersistedInt()} can't return a persisted value.</p> 1037 1038<p class="caution"><strong>Caution:</strong> You <strong>cannot</strong> use the 1039<code>defaultValue</code> as the default value in the {@code getPersisted*()} method, because 1040its value is always null when <code>restorePersistedValue</code> is <code>true</code>.</p> 1041 1042 1043<h3 id="CustomDefault">Providing a default value</h3> 1044 1045<p>If the instance of your {@link android.preference.Preference} class specifies a default value 1046(with the {@code android:defaultValue} attribute), then the 1047system calls {@link android.preference.Preference#onGetDefaultValue 1048onGetDefaultValue()} when it instantiates the object in order to retrieve the value. You must 1049implement this method in order for the system to save the default value in the {@link 1050android.content.SharedPreferences}. For example:</p> 1051 1052<pre> 1053@Override 1054protected Object onGetDefaultValue(TypedArray a, int index) { 1055 return a.getInteger(index, DEFAULT_VALUE); 1056} 1057</pre> 1058 1059<p>The method arguments provide everything you need: the array of attributes and the index 1060position of the {@code android:defaultValue}, which you must retrieve. The reason you must 1061implement this method to extract the default value from the attribute is because you must specify 1062a local default value for the attribute in case the value is undefined.</p> 1063 1064 1065 1066<h3 id="CustomSaveState">Saving and restoring the Preference's state</h3> 1067 1068<p>Just like a {@link android.view.View} in a layout, your {@link android.preference.Preference} 1069subclass is responsible for saving and restoring its state in case the activity or fragment is 1070restarted (such as when the user rotates the screen). To properly save and 1071restore the state of your {@link android.preference.Preference} class, you must implement the 1072lifecycle callback methods {@link android.preference.Preference#onSaveInstanceState 1073onSaveInstanceState()} and {@link 1074android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()}.</p> 1075 1076<p>The state of your {@link android.preference.Preference} is defined by an object that implements 1077the {@link android.os.Parcelable} interface. The Android framework provides such an object for you 1078as a starting point to define your state object: the {@link 1079android.preference.Preference.BaseSavedState} class.</p> 1080 1081<p>To define how your {@link android.preference.Preference} class saves its state, you should 1082extend the {@link android.preference.Preference.BaseSavedState} class. You need to override just 1083 a few methods and define the {@link android.preference.Preference.BaseSavedState#CREATOR} 1084object.</p> 1085 1086<p>For most apps, you can copy the following implementation and simply change the lines that 1087handle the {@code value} if your {@link android.preference.Preference} subclass saves a data 1088type other than an integer.</p> 1089 1090<pre> 1091private static class SavedState extends BaseSavedState { 1092 // Member that holds the setting's value 1093 // Change this data type to match the type saved by your Preference 1094 int value; 1095 1096 public SavedState(Parcelable superState) { 1097 super(superState); 1098 } 1099 1100 public SavedState(Parcel source) { 1101 super(source); 1102 // Get the current preference's value 1103 value = source.readInt(); // Change this to read the appropriate data type 1104 } 1105 1106 @Override 1107 public void writeToParcel(Parcel dest, int flags) { 1108 super.writeToParcel(dest, flags); 1109 // Write the preference's value 1110 dest.writeInt(value); // Change this to write the appropriate data type 1111 } 1112 1113 // Standard creator object using an instance of this class 1114 public static final Parcelable.Creator<SavedState> CREATOR = 1115 new Parcelable.Creator<SavedState>() { 1116 1117 public SavedState createFromParcel(Parcel in) { 1118 return new SavedState(in); 1119 } 1120 1121 public SavedState[] newArray(int size) { 1122 return new SavedState[size]; 1123 } 1124 }; 1125} 1126</pre> 1127 1128<p>With the above implementation of {@link android.preference.Preference.BaseSavedState} added 1129to your app (usually as a subclass of your {@link android.preference.Preference} subclass), you 1130then need to implement the {@link android.preference.Preference#onSaveInstanceState 1131onSaveInstanceState()} and {@link 1132android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()} methods for your 1133{@link android.preference.Preference} subclass.</p> 1134 1135<p>For example:</p> 1136 1137<pre> 1138@Override 1139protected Parcelable onSaveInstanceState() { 1140 final Parcelable superState = super.onSaveInstanceState(); 1141 // Check whether this Preference is persistent (continually saved) 1142 if (isPersistent()) { 1143 // No need to save instance state since it's persistent, use superclass state 1144 return superState; 1145 } 1146 1147 // Create instance of custom BaseSavedState 1148 final SavedState myState = new SavedState(superState); 1149 // Set the state's value with the class member that holds current setting value 1150 myState.value = mNewValue; 1151 return myState; 1152} 1153 1154@Override 1155protected void onRestoreInstanceState(Parcelable state) { 1156 // Check whether we saved the state in onSaveInstanceState 1157 if (state == null || !state.getClass().equals(SavedState.class)) { 1158 // Didn't save the state, so call superclass 1159 super.onRestoreInstanceState(state); 1160 return; 1161 } 1162 1163 // Cast state to custom BaseSavedState and pass to superclass 1164 SavedState myState = (SavedState) state; 1165 super.onRestoreInstanceState(myState.getSuperState()); 1166 1167 // Set this Preference's widget to reflect the restored state 1168 mNumberPicker.setValue(myState.value); 1169} 1170</pre> 1171 1172