Applications often include settings that allow users to modify app features and behaviors. For example, some apps allow users to specify whether notifications are enabled or specify how often the application syncs data with the cloud.
> Settings Design
For information about how to design your settings, read theSettings design guide.
》Because your app's settings UI is built using Preference
objects instead of View
objects, you need to use a specialized Activity
or Fragment
subclass to display the list settings:
- If your app supports versions of Android older than 3.0 (API level 10 and lower), you must build the activity as an extension of the
PreferenceActivity
class. - On Android 3.0 and later, you should instead use a traditional
Activity
that hosts aPreferenceFragment
that displays your app settings. However, you can also usePreferenceActivity
to create a two-pane layout for large screens when you have multiple groups of settings.
<PreferenceScreen>
element. Within this element is where you add each
Preference
. Each child you add within the
<PreferenceScreen>
element appears as a single item in the list of settings.
For example:
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <CheckBoxPreference android:key="pref_sync" android:title="@string/pref_sync" android:summary="@string/pref_sync_summ" android:defaultValue="true" /> <ListPreference android:dependency="pref_sync" android:key="pref_syncConnectionType" android:title="@string/pref_syncConnectionType" android:dialogTitle="@string/pref_syncConnectionType" android:entries="@array/pref_syncConnectionTypes_entries" android:entryValues="@array/pref_syncConnectionTypes_values" android:defaultValue="@string/pref_syncConnectionTypes_default" /> </PreferenceScreen>
》If you want to provide dividers with headings between groups of settings (as shown in figure 2), place each group of Preference
objects inside a PreferenceCategory
.
For example:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceCategory android:title="@string/pref_sms_storage_title" android:key="pref_key_storage_settings"> <CheckBoxPreference android:key="pref_key_auto_delete" android:summary="@string/pref_summary_auto_delete" android:title="@string/pref_title_auto_delete" android:defaultValue="false"... /> <Preference android:key="pref_key_sms_delete_limit" android:dependency="pref_key_auto_delete" android:summary="@string/pref_summary_delete_limit" android:title="@string/pref_title_sms_delete"... /> <Preference android:key="pref_key_mms_delete_limit" android:dependency="pref_key_auto_delete" android:summary="@string/pref_summary_delete_limit" android:title="@string/pref_title_mms_delete" ... /> </PreferenceCategory> ... </PreferenceScreen>》 Using subscreens
If you want to place groups of settings into a subscreen (as shown in figure 3), place the group of Preference
objects inside a PreferenceScreen
.

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <!-- opens a subscreen of settings --> <PreferenceScreen android:key="button_voicemail_category_key" android:title="@string/voicemail" android:persistent="false"> <ListPreference android:key="button_voicemail_provider_key" android:title="@string/voicemail_provider" ... /> <!-- opens another nested subscreen --> <PreferenceScreen android:key="button_voicemail_setting_key" android:title="@string/voicemail_settings" android:persistent="false"> ... </PreferenceScreen> <RingtonePreference android:key="button_voicemail_ringtone_key" android:title="@string/voicemail_ringtone_title" android:ringtoneType="notification" ... /> ... </PreferenceScreen> ... </PreferenceScreen>》 In some cases, you might want a preference item to open a different activity instead of a settings screen, such as a web browser to view a web page. To invoke an
Intent
when the user selects a preference item, add an
<intent>
element as a child of the corresponding
<Preference>
element.
For example, here's how you can use a preference item to open a web page:
<Preference android:title="@string/prefs_web_page" > <intent android:action="android.intent.action.VIEW" android:data="http://www.example.com" /> </Preference>
PreferenceFragment
. Go to the next section about Using Preference Fragments.
public class SettingsActivity extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); } }
PreferenceFragment
to display your list of
Preference
objects. You can add a
PreferenceFragment
to any activity—you don't need to use
PreferenceActivity
.
> Even if your application supports versions of Android older than 3.0, you can build your application to usePreferenceFragment
for a two-pane presentation on newer devices while still supporting a traditional multi-screen hierarchy on older devices (see the section about Supporting older versions with preference headers).
public static class SettingsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String settings = getArguments().getString("settings"); if ("notifications".equals(settings)) { addPreferencesFromResource(R.xml.settings_wifi); } else if ("sync".equals(settings)) { addPreferencesFromResource(R.xml.settings_sync); } } }
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <Preference android:title="@string/prefs_category_one" android:summary="@string/prefs_summ_category_one" > <intent android:targetPackage="com.example.prefs" android:targetClass="com.example.prefs.SettingsActivity" android:action="com.example.prefs.PREFS_ONE" /> </Preference> <Preference android:title="@string/prefs_category_two" android:summary="@string/prefs_summ_category_two" > <intent android:targetPackage="com.example.prefs" android:targetClass="com.example.prefs.SettingsActivity" android:action="com.example.prefs.PREFS_TWO" /> </Preference> </PreferenceScreen>> There are several reasons you might want to be notified as soon as the user changes one of the preferences. In order to receive a callback when a change happens to any one of the preferences, implement the
SharedPreference.OnSharedPreferenceChangeListener
interface and register the listener for the
SharedPreferences
object by calling
registerOnSharedPreferenceChangeListener()
.
The interface has only one callback method, onSharedPreferenceChanged()
, and you might find it easiest to implement the interface as a part of your activity. For example:
public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener { public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType"; ... public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(KEY_PREF_SYNC_CONN)) { Preference connectionPref = findPreference(key); // Set summary to be the user-description for the selected value connectionPref.setSummary(sharedPreferences.getString(key, "")); } } }
@Override protected void onResume() { super.onResume(); getPreferenceScreen().getSharedPreferences() .registerOnSharedPreferenceChangeListener(this); } @Override protected void onPause() { super.onPause(); getPreferenceScreen().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener(this); }
Instead, store a reference to the listener in an instance data field of an object that will exist as long as the listener is needed:
SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // listener implementation } }; prefs.registerOnSharedPreferenceChangeListener(listener);> Once you've added the necessary preferences in your
PreferenceActivity
to control your app's data habits, you should add an intent filter for
ACTION_MANAGE_NETWORK_USAGE
in your manifest file. For example:
<activity android:name="SettingsActivity" ... > <intent-filter> <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
This intent filter indicates to the system that this is the activity that controls your application's data usage. Thus, when the user inspects how much data your app is using from the system's Settings app, a View application settings button is available that launches your PreferenceActivity
so the user can refine how much data your app uses.
> In such a case, you’ll need to create a custom preference by extending the Preference
class or one of the other subclasses.
When you extend the Preference
class, there are a few important things you need to do:
- Specify the user interface that appears when the user selects the settings.
- Save the setting's value when appropriate.
- Initialize the
Preference
with the current (or default) value when it comes into view. - Provide the default value when requested by the system.
- If the
Preference
provides its own UI (such as a dialog), save and restore the state to handle lifecycle changes (such as when the user rotates the screen).
>To properly save and restore the state of your Preference
class, you must implement the lifecycle callback methods onSaveInstanceState()
and onRestoreInstanceState()
.
To define how your Preference
class saves its state, you should extend the Preference.BaseSavedState
class. You need to override just a few methods and define the CREATOR
object.
> For most apps, you can copy the following implementation and simply change the lines that handle the value
if your Preference
subclass saves a data type other than an integer.
private static class SavedState extends BaseSavedState { // Member that holds the setting's value // Change this data type to match the type saved by your Preference int value; public SavedState(Parcelable superState) { super(superState); } public SavedState(Parcel source) { super(source); // Get the current preference's value value = source.readInt(); // Change this to read the appropriate data type } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); // Write the preference's value dest.writeInt(value); // Change this to write the appropriate data type } // Standard creator object using an instance of this class public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; }
With the above implementation of Preference.BaseSavedState
added to your app (usually as a subclass of your Preference
subclass), you then need to implement the onSaveInstanceState()
andonRestoreInstanceState()
methods for your Preference
subclass.
For example:
@Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); // Check whether this Preference is persistent (continually saved) if (isPersistent()) { // No need to save instance state since it's persistent, // use superclass state return superState; } // Create instance of custom BaseSavedState final SavedState myState = new SavedState(superState); // Set the state's value with the class member that holds current // setting value myState.value = mNewValue; return myState; } @Override protected void onRestoreInstanceState(Parcelable state) { // Check whether we saved the state in onSaveInstanceState if (state == null || !state.getClass().equals(SavedState.class)) { // Didn't save the state, so call superclass super.onRestoreInstanceState(state); return; } // Cast state to custom BaseSavedState and pass to superclass SavedState myState = (SavedState) state; super.onRestoreInstanceState(myState.getSuperState()); // Set this Preference's widget to reflect the restored state mNumberPicker.setValue(myState.value); }