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