Chapter    11

Working with Preferences and Saving State

Android offers a robust and flexible framework for dealing with settings, also known as preferences. And by settings, we mean those feature choices that a user makes and saves to customize an application to their liking. (In this chapter, the terms settings and preferences will be used interchangeably.) For example, if the user wants a notification via a ringtone or vibration or not at all, that is a preference the user saves; the application remembers the choice until the user changes it. Android provides simple APIs that hide the management and persisting of preferences. It also provides prebuilt user interfaces that you can use to let the user make preference selections. Because of the power built into the Android preferences framework, we can also use preferences for more general-purpose storing of application state, to allow our application to pick up where it left off, should our application go away and come back later. As another example, a game’s high scores could be stored as preferences, although you’ll want to use your own UI to display them.

This chapter covers how to implement your own settings screens for your application, how to interact with Android system settings, and how to use settings to secretly save application state, and it also provides best-practice guidance. You’ll discover how to make your settings look good on small screens as well as larger screens such as those found on tablets.

Exploring the Preferences Framework

Android’s preferences framework builds from the individual settings choices, to a hierarchy of screens that contain settings choices. Settings could be binary settings such as on/off, or text input, or a numeric value, or could be a selection from a list of choices. Android uses a PreferenceManager to provide settings values to applications. The framework takes care of making and persisting changes, and notifying the application when a setting changes or is about to change. While settings are persisted in files, applications don’t deal directly with the files. The files are hidden away, and you’ll see shortly where they are.

As with views covered in Chapter 3, preferences can be specified with XML, or by writing code. For this chapter, you’ll work with a sample application that demonstrates the different types of choices. XML is the preferred way to specify a preference, so that is how the application was written. XML specifies the lowest-level settings, plus how to group settings together into categories and screens. For reference, the sample application for this chapter presents the following settings as shown in Figure 11-1.

9781430246800_Fig11-01.jpg

Figure 11-1. The main settings from the sample app preference UI. Due to the screen’s height, it has been shown with the top on the left and the bottom on the right. Notice the overlap between the two images

Android provides an end-to-end preferences framework. This means the framework lets you define your preferences, display the setting(s) to the user, and persist the user’s selection to the data store. You define your preferences in XML under /res/xml/. To show preferences to the user, you write an activity class that extends a predefined Android class called android.preference.PreferenceActivity and use fragments to handle the screens of preferences. The framework takes care of the rest (displaying and persisting). Within your application, your code will get references to specific preferences. With a preference reference, you can get the current value of the preference.

In order for preferences to be saved across user sessions, the current values must be saved somewhere. The Android framework takes care of persisting preferences in an XML file within the application’s /data/data directory on the device (see Figure 11-2).

9781430246800_Fig11-02.jpg

Figure 11-2. Path to an application’s saved preferences

Note  You will be able to inspect shared preferences files in the emulator only. On a real device, the shared preferences files are not readable due to Android security (unless you have root privileges, of course).

The default preferences file path for an application is /data/data/[PACKAGE_NAME]/shared_prefs/[PACKAGE_NAME]_preferences.xml, where [PACKAGE_NAME] is the package of the application. Listing 11-1 shows the com.androidbook.preferences.main_preferences.xml data file for this example.

Listing 11-1. Saved Preferences for Our Example

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<boolean name="notification_switch" value="true" />
<string name="package_name_preference">com.androidbook.win</string>
<boolean name="potato_selection_pref" value="true" />
<boolean name="show_airline_column_pref" value="true" />
<string name="flight_sort_option">2</string>
<boolean name="alert_email" value="false" />
<set name="pizza_toppings">
<string>pepperoni</string>
<string>cheese</string>
<string>olive</string>
</set>
<string name="alert_email_address">[email protected]</string>
</map>

As you can see, values are stored in a map, with preference keys as names to the data values. Some of the values look cryptic and do not match what is displayed to the user. For example, the value for flight_sort_option is 2. Android does not store the displayed text as the value of the preference; rather, it stores a value that the user won’t see, that you can use independently of what the user sees. You want the freedom to change the displayed text based on the user’s language, and you also want the ability to tweak the displayed text while keeping the same stored value in the preferences file. You might even be able to do simpler processing of the preference if the value is an integer instead of some display string. What you don’t have to worry about is parsing this data file. The Android preferences framework provides a nice API for dealing with preferences, which will be described in more detail later in this chapter.

If you compare the preferences map in Listing 11-1 with the screenshots in Figure 11-1, you will notice that not all preferences are listed with values in the preferences XML data file. This is because the preference data file does not automatically store a default value for you. You’ll see shortly how to deal with default values.

Now that you’ve seen where the values are saved, you need to see how to define the screens to display to the user so they can make selections. Before you see how to collect preferences together into screens, you’ll learn about the different types of preferences you can use, and then you’ll see how to put them together into screens. Each persisted value in the /data/data XML file is from a specific preference. So let’s understand what each of these means.

Understanding CheckBoxPreference and SwitchPreference

The simplest of the preferences are the CheckBoxPreference and SwitchPreference. These share a common parent class (TwoStatePreference) and are either on (value is true) or off (value is false). For the sample application, a screen was created with five CheckBoxPreferences, as shown in Figure 11-3. Listing 11-2 shows what the XML looks like for a CheckBoxPreference.

9781430246800_Fig11-03.jpg

Figure 11-3. The user interface for the check box preference

Listing 11-2. Using CheckBoxPreference

<CheckBoxPreference
        android:key="show_airline_column_pref"
        android:title="Airline"
        android:summary="Show Airline column" />

Note  We will give you a URL at the end of the chapter that you can use to download projects from this chapter. This will allow you to import these projects into your IDE directly. The main sample application is called PrefDemo. You should refer to that project until you come to the Saving State section.

This example shows the minimum that’s required to specify a preference. The key is the reference to, or name of, the preference, the title is the title displayed for the preference, and summary is a description of what the preference is for or a status of the current setting. Looking back on the saved values in Listing 11-1, you will see a <boolean> tag for “show_airline_column_pref” (the key), and it has an attribute value of true, which indicates that the preference is checked on.

With CheckBoxPreference, the state of the preference is saved when the user sets the state. In other words, when the user checks or unchecks the preference control, its state is saved immediately.

The SwitchPreference is very similar except that the visual display is different. Instead of a check box in the user interface, the user sees an on-off switch, as shown in Figure 11-1 next to “Notifications are”.

One other useful feature of CheckBoxPreference and SwitchPreference is that you can set different summary text depending on whether it’s checked. The XML attributes are summaryOn and summaryOff. If you look in the main.xml file for the CheckBoxPreference called “potato_selection_pref” you will see an example of this.

Before you learn the other preference types, now would be a good time to understand how to access this preference to read its value and perform other operations.

Accessing a Preference Value in Code

Now that you have a preference defined you need to know how to access the preference in code so you can read the value. Listing 11-3 shows code to access the SharedPreferences object in Android where the preferences exist. This code is from the MainActivity.java file in the setOptionText() method.

Listing 11-3. Accessing the CheckBoxPreference

    SharedPreferences prefs =
            PreferenceManager.getDefaultSharedPreferences(this);
//  This is the other way to get to the shared preferences:
//  SharedPreferences prefs = getSharedPreferences(
//          "com.androidbook.preferences.main_preferences", 0);
    boolean showAirline = prefs.getBoolean("show_airline_column_pref", false);

Using the reference to preferences, it is straightforward to read the current value of the show_airline_column_pref preference. As shown in Listing 11-3, there are two ways to get to the preferences. The first way shown is to get the default preferences for the current context. In this case, the context is that of the MainActivity of our application. The second case, which is shown commented out, retrieves the preferences using a package name. You could use whatever package name you want in case you need to store different sets of preferences in different files.

Once you have a reference to the preferences, you call the appropriate getter method with the key of the preference and a default value. Since show_airline_column_pref is a TwoStatePreference, the value returned is a boolean. The default value for show_airline_column_pref is hard-coded here as false. If this preference has not yet been set at all, the hard-coded value (false) will be assigned to showAirline. However, that by itself does not persist the preference to false for future use, nor does it honor any default value that might have been set in the XML specification for this preference. If the XML specification uses a resource value to specify the default value, then the same resource could be referred to in code to set the default value, as shown in the following for a different preference:

String flight_option = prefs.getString(
        resources.getString(R.string.flight_sort_option),
        resources.getString(R.string.flight_sort_option_default_value));

Notice here that the key for the preference is also using a string resource value (R.string.flight_sort_option). This can be a wise choice since it makes typos less likely. If the resource name is typed wrong you’ll very likely get a build error. If you use just simple strings, it is possible for a typo to go unnoticed, except that your preferences won’t work.

We showed one way to read a default value for a preference in code. Android provides another way that is a bit more elegant. In onCreate(), you can do the following instead:

PreferenceManager.setDefaultValues(this, R.xml.main, false);

Then, in setOptionText(), you would have done this to read the option value:

String option = prefs.getString(
    resources.getString(R.string.flight_sort_option), null);

The first call will use main.xml to find the default values and generate the preferences XML data file for us using the default values. If we already have an instance of the SharedPreferences object in memory, it will update that too. The second call will then find a value for flight_sort_option, because we took care of loading defaults first.

After running this code the first time, if you look in the shared_prefs folder, you will see the preferences XML file even if the preferences screen has not yet been invoked. You will also see another file called _has_set_default_values.xml. This file tells your application that the preferences XML file has already been created with the default values. The third argument to setDefaultValues()—that is, false—indicates that you want the defaults set in the preferences XML file only if it hasn’t been done before. Android remembers this information through the existence of this new XML file. However, Android remembers even if you upgrade your application and add new settings with new default values, which means this trick won’t set those new defaults. Your best option is to always use a resource for the default value, and always provide that resource as the default value when getting the current value of a preference.

Understanding ListPreference

A list preference contains radio buttons for each option, and the default (or current) selection is preselected. The user is expected to select one and only one of the choices. When the user chooses an option, the dialog is immediately dismissed and the choice is saved in the preferences XML file. Figure 11-4 shows what this looks like.

9781430246800_Fig11-04.jpg

Figure 11-4. The user interface for the ListPreference

Listing 11-4 contains an XML fragment that represents the flight-option preference setting. This time the file contains references to strings and to arrays, which would be the more common way to specify these rather than hard-coding the strings. As mentioned before, the value of a list preference as stored in the XML data file under the /data/data/{package} directory is not the same as what the user sees in the user interface. The name of the key is stored in the data file, along with a hidden value that the user does not see. Therefore, to get a ListPreference to work, there needs to be two arrays: the values displayed to the user and the strings used as key values. This is where you can easily get tripped up. The entries array holds the strings displayed to the user, and the entryValues array holds the strings that will be stored in the preferences data XML file.

Listing 11-4. Specifying a ListPreference in XML

<ListPreference
  android:key="@string/flight_sort_option"
  android:title="@string/listTitle"
  android:summary="@string/listSummary"
  android:entries="@array/flight_sort_options"
  android:entryValues="@array/flight_sort_options_values"
  android:dialogTitle="@string/dialogTitle"
  android:defaultValue="@string/flight_sort_option_default_value" />

The elements between the two arrays correspond to each other positionally. That is, the third element in the entryValues array corresponds to the third element in the entries array. It is tempting to use 0, 1, 2, etc., as entryValues but it is not required, and it could cause problems later when the arrays must be modified. If our option were numeric in nature (for example, a countdown timer starting value), then we could have used values such as 60, 120, 300, and so on. The values don’t need to be numeric at all as long as they make sense to the developer; the user doesn’t see these values unless you choose to expose them. The user only sees the text from the first string array flight_sort_options. The example application for this chapter shows it both ways.

A word of caution here: because the preferences XML data file is storing only the value and not the text, should you ever upgrade your application and change the text of the options or add items to the string arrays, any value stored in the preferences XML data file should still line up with the appropriate text after the upgrade. The preferences XML data file is kept during the application upgrade. If the preferences XML data file had a "1" in it, and that meant “# of Stops” before the upgrade, it should still mean “# of Stops” after the upgrade.

Since the entryValues array is not seen by the end user, it is best practice to store it once and only once within your application. Therefore, make one and only one /res/values/prefvaluearrays.xml file to contain these arrays. The entries array is very likely to be created multiple times per application, for different languages or perhaps different device configurations. Therefore, make separate prefdisplayarrays.xml files for each variation that you need. For example, if your application will be used in English and in French, there will be separate prefdisplayarrays.xml files for English and French. You do not want to include the entryValues array in each of these other files. It is imperative though that there are the same numbers of array elements between entryValues and entries arrays. The elements must line up. When you make changes, be careful to keep everything in alignment. Listing 11-5 contains the source of ListPreference files for the example.

Listing 11-5. Other ListPreference Files from Our Example

<?xml version="1.0" encoding="utf-8"?>
<!-- This file is /res/values/prefvaluearrays.xml -->
<resources>
<string-array name="flight_sort_options_values">
    <item>0</item>
    <item>1</item>
    <item>2</item>
</string-array>
<string-array name="pizza_toppings_values">
    <item>cheese</item>
    <item>pepperoni</item>
    <item>onion</item>
    <item>mushroom</item>
    <item>olive</item>
    <item>ham</item>
    <item>pineapple</item>
</string-array>
<string-array name="default_pizza_toppings">
    <item>cheese</item>
    <item>pepperoni</item>
</string-array>
</resources>

<?xml version="1.0" encoding="utf-8"?>
<!-- This file is /res/values/prefdisplayarrays.xml -->
<resources>
<string-array name="flight_sort_options">
    <item>Total Cost</item>
    <item># of Stops</item>
    <item>Airline</item>
</string-array>
<string-array name="pizza_toppings">
    <item>Cheese</item>
    <item>Pepperoni</item>
    <item>Onions</item>
    <item>Portobello Mushrooms</item>
    <item>Black Olives</item>
    <item>Smoked Ham</item>
    <item>Pineapple</item>
</string-array>
</resources>

Also, don’t forget that your default value as specified in the XML source file must match an entryValue in the array from prefvaluearrays.xml.

For a ListPreference, the value of the preference is a String. If you are using number strings (e.g., 0, 1, 1138) as entryValues, you could convert those to integers or whatever you need in your code, as is used in the flight_sort_options_values array.

Your code is likely going to want to display the user-friendly text from the preference’s entries array. This example took a shortcut, because array indices were used for the elements in flight_sort_options_values. By simply converting the value to an int, you know which string to read from flight_sort_options. Had you used some other set of values for flight_sort_options_values, you would need to determine the index of the element that is your preference and then turn around and use that index to grab the text of your preference from flight_sort_options. ListPreference’s helper method findIndexOfValue() can help with this, by providing the index into the values array so you can then easily get the corresponding display text from the entries array.

Returning now to Listing 11-4, there are several strings for titles, summaries, and more. The string called flight_sort_option_default_value sets the default value to 1 to represent “# of Stops” in the example. It is usually a good idea to choose a default value for each option. If you don’t choose a default value and no value has yet been chosen, the methods that return the value of the option will return null. Your code would have to deal with null values in this case.

Understanding EditTextPreference

The preferences framework also provides a free-form text preference called EditTextPreference. This preference allows you to capture raw text rather than ask the user to make a selection. To demonstrate this, let’s assume you have an application that generates Java code for the user. One of the preference settings of this application might be the default package name to use for the generated classes. Here, you want to display a text field to the user for setting the package name for the generated classes. Figure 11-5 shows the UI, and Listing 11-6 shows the XML.

9781430246800_Fig11-05.jpg

Figure 11-5. Using the EditTextPreference

Listing 11-6. An Example of an EditTextPreference

<EditTextPreference
        android:key="package_name_preference"
        android:title="Set Package Name"
        android:summary="Set the package name for generated code"
        android:dialogTitle="Package Name" />

When Set Package Name is selected, the user is presented with a dialog to input the package name. When the OK button is clicked, the preference is saved to the preference store.

As with the other preferences, you can obtain the value of the preference by calling the appropriate getter method, in this case getString().

Understanding MultiSelectListPreference

And finally, a preference called MultiSelectListPreference was introduced in Android 3.0. The concept is somewhat similar to a ListPreference, but instead of only being able to select one item in the list, the user can select several or none. In Listing 11-1, the MultiSelectListPreference stores a <set name="pizza_toppings"> tag in the preferences XML data file, instead of a single value. The other significant difference with a MultiSelectListPreference is that the default value is an array just like the entryValues array. That is, the array for the default values must contain zero or more of the elements from the entryValues array for this preference. This can also be seen in the sample application for this chapter; just view the end of the main.xml file in the /res/xml directory.

To get the current value of a MultiSelectListPreference, use the getStringSet() method of SharedPreferences. To retrieve the display strings from the entries array, you would need to iterate through the set of strings that is the value of this preference, determine the index of the string, and use the index to access the proper display string from the entries array.

Updating AndroidManifest.xml

Because there are two activities in the sample application, we need two activity tags in AndroidManifest.xml. The first one is a standard activity of category LAUNCHER. The second one is for a PreferenceActivity, so set the action name according to convention for intents, and set the category to PREFERENCE as shown in Listing 11-7. You probably don’t want the PreferenceActivity showing up on the Android page with all our other applications, which is why you don’t use LAUNCHER for it. You would need to make similar changes to AndroidManifest.xml if you were to add other preference activities.

Listing 11-7. PreferenceActivity Entry in AndroidManifest.xml

        <activity android:name=".MainPreferenceActivity"
                  android:label="@string/prefTitle">
            <intent-filter>
                <action android:name=
 "com.androidbook.preferences.main.intent.action.MainPreferences" />
                <category
                    android:name="android.intent.category.PREFERENCE" />
            </intent-filter>
        </activity>

Using PreferenceCategory

The preferences framework provides support for you to organize your preferences into categories. If you have a lot of preferences, for example, you can use PreferenceCategory, which groups preferences under a separator label. Figure 11-6 shows what this could look like. Notice the separators called “MEATS” and “VEGETABLES.” You can find the specifications for these in /res/xml/main.xml.

9781430246800_Fig11-06.jpg

Figure 11-6. Using PreferenceCategory to organize preferences

Creating Child Preferences with Dependency

Another way to organize preferences is to use a preference dependency. This creates a parent-child relationship between preferences. For example, you might have a preference that turns on alerts; and if alerts are on, there might be several other alert-related preferences to choose from. If the main alerts preference is off, the other preferences are not relevant and should be disabled. Listing 11-8 shows the XML, and Figure 11-7 shows what it looks like.

Listing 11-8. Preference Dependency in XML

<PreferenceScreen>
    <PreferenceCategory
            android:title="Alerts">

        <CheckBoxPreference
                android:key="alert_email"
                android:title="Send email?" />

        <EditTextPreference
                android:key="alert_email_address"
                android:layout="?android:attr/preferenceLayoutChild"
                android:title="Email Address"
                android:dependency="alert_email" />

    </PreferenceCategory>
</PreferenceScreen>

9781430246800_Fig11-07.jpg

Figure 11-7. Preference dependency

Preferences with Headers

Android 3.0 introduced a new way to organize preferences. You see this on tablets under the main Settings app. Because tablet screen real estate offers much more room than a smartphone does, it makes sense to display more preference information at the same time. To accomplish this, you use preference headers. Take a look at Figure 11-8.

9781430246800_Fig11-08.jpg

Figure 11-8. Main Settings page with preference headers

Notice that headers appear down the left side, like a vertical tab bar. As you click each item on the left, the screen to the right displays the preferences for that item. In Figure 11-8, Sound is chosen, and the sound preferences are displayed at right. The right side is a PreferenceScreen object, and this setup uses fragments. Obviously, we need to do something different than what has been discussed so far in this chapter.

The big change from Android 3.0 was the addition of headers to PreferenceActivity. This also means using a new callback within PreferenceActivity to do the headers setup. Now, when you extend PreferenceActivity, you’ll want to implement this method:

public void onBuildHeaders(List<Header> target) {
    loadHeadersFromResource(R.xml.preferences, target);
}

Please refer to the PrefDemo sample application for the complete source code. The preferences.xml file contains some new tags that look like this:

<preference-headers
        xmlns:android="http://schemas.android.com/apk/res/android">
    <header android:fragment="com.example.PrefActivity$Prefs1Fragment"
            android:icon="@drawable/ic_settings_sound"
            android:title="Sound"
            android:summary="Your sound preferences" />
    ...

Each header tag points to a class that extends PreferenceFragment. In the example just given, the XML specifies an icon, the title, and summary text (which acts like a subtitle). Prefs1Fragment is an inner class of PreferenceActivity that could look something like this:

public static class Prefs1Fragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.sound_preferences);
    }
}

All this inner class needs to do is pull in the appropriate preferences XML file, as shown. That preferences XML file contains the types of preference specifications we covered earlier, such as ListPreference, CheckBoxPreference, PreferenceCategory, and so on. What’s very nice is that Android takes care of doing the right thing when the screen configuration changes and when the preferences are displayed on a small screen. Headers behave like old preferences when the screen is too small to display both headers and the preference screen to the right. That is, you only see the headers; and when you click a header, you then see only the appropriate preference screen.

PreferenceScreens

The top-level container for preferences is a PreferenceScreen. Before tablets and PreferenceFragments, you could nest PreferenceScreens, and when the user clicked on a nested PreferenceScreen item, the new PreferenceScreen would replace the currently displayed PreferenceScreen. This worked fine on a small screen, but doesn’t look as good on a tablet, especially if you started with headers and fragments. What you probably want is for the new PreferenceScreen to appear where the current fragment is.

To make a PreferenceScreen work inside of a fragment, all you need to do is specify a fragment class name for that PreferenceScreen. Listing 11-9 shows the XML from the sample application.

Listing 11-9. PreferenceScreen invoked via a PreferenceFragment

<PreferenceScreen
    android:title="Launch a new screen into a fragment"
    android:fragment="com.androidbook.preferences.main.BasicFrag" />

When the user clicks on this item, the current fragment is replaced with BasicFrag, which then loads a new XML layout for a PreferenceScreen as specified in nested_screen_basicfrag.xml. In this case, we chose not to make the BasicFrag class an inner class of the MainPreferenceActivity class, mainly because there is no sharing needed from the outer class, and to show you that you can do it this way if you prefer.

Dynamic Preference Summary Text

You’ve probably seen preferences where the preference summary contains the current value. This is actually a little harder to implement than you might think. To accomplish this feat, you create a listener callback that detects when a preference value is about to change, and you then update the preference summary accordingly. The first step is for your PreferenceFragment to implement the OnPreferenceChangeListener interface. You then need to implement the onPreferenceChange() callback. Listing 11-10 shows an example. The pkgPref object in the callback was set earlier to the preference in the onCreate() method.

Listing 11-10. Setting Up a Preference Listener

public boolean onPreferenceChange(Preference preference,
                                      Object newValue) {
    final String key = preference.getKey();
    if ("package_name_preference".equals(key)) {
        pkgPref.setSummary(newValue.toString());
    }
    ...
    return true;
}

You have to register the fragment as a listener in onResume() using setOnPreferenceChangeListener(this) on each preference you want to listen on, and unregister in onPause() by calling it again with null. Now every time there is a pending change to a preference you’ve registered for, this callback will be invoked passing in the preference and the potential new value. The callback returns a boolean indicating whether to proceed with setting the preference to the new value (true) or not (false). Assuming you would return true to allow the new setting, this is where you can update the summary value as well. You could also validate the new value and reject the change. Perhaps you want a MultiSelectListPreference to have a maximum number of checked items. You could count the selected items in the callback and reject the change if there are too many.

Saving State with Preferences

Preferences are great for allowing users to customize applications to their liking, but we can use the Android preference framework for more than that. When your application needs to keep track of some data between invocations of the application, preferences are one way to accomplish the task even if the user can’t see the data in preference screens. Please find the sample application called SavingStateDemo to follow along with the complete source code.

The Activity class has a getPreferences(int mode) method. This, in reality, simply calls getSharedPreferences() with the class name of the activity as the tag plus the mode as passed in. The result is an activity-specific shared preferences file that you can use to store data about this activity across invocations. A simple example of how you could use this is shown in Listing 11-11.

Listing 11-11. Using Preferences to Save State for an Activity

    final String INITIALIZED = "initialized";
    private String someString;

[ ... ]

    SharedPreferences myPrefs = getPreferences(MODE_PRIVATE);

    boolean hasPreferences = myPrefs.getBoolean(INITIALIZED, false);
    if(hasPreferences) {
        Log.v("Preferences", "We've been called before");
        // Read other values as desired from preferences file...
        someString = myPrefs.getString("someString", "");
    }
    else {
        Log.v("Preferences", "First time ever being called");
        // Set up initial values for what will end up
        // in the preferences file
        someString = "some default value";
    }

[ ... ]

    // Later when ready to write out values
    Editor editor = myPrefs.edit();
    editor.putBoolean(INITIALIZED, true);
    editor.putString("someString", someString);
    // Write other values as desired
    editor.commit();

What this code does is acquire a reference to preferences for our activity class and check for the existence of a boolean “preference” called initialized. We write “preference” in double quotation marks because this value is not something the user is going to see or set; it’s merely a value that we want to store in a shared preferences file for use next time. If we get a value, the shared preferences file exists, so the application must have been called before. You could then read other values out of the shared preferences file. For example, someString could be an activity variable that should be set from the last time this activity ran or set to the default value if this is the first time.

To write values to the shared preferences file, you must first get a preferences Editor. You can then put values into preferences and commit those changes when you’re finished. Note that, behind the scenes, Android is managing a SharedPreferences object that is truly shared. Ideally, there is never more than one Editor active at a time. But it is very important to call the commit() method so that the SharedPreferences object and the shared preferences XML file get updated. In the example, the value of someString is written out to be used the next time this activity runs.

You can access, write, and commit values any time to your preferences file. Possible uses for this include writing out high scores for a game or recording when the application was last run. You can also use the getSharedPreferences() call with different names to manage separate sets of preferences, all within the same application or even the same activity.

MODE_PRIVATE was used for mode in our examples thus far. Because the shared preferences files are always stored within your application’s /data/data/{package} directory and therefore are not accessible to other applications, you only need to use MODE_PRIVATE.

Using DialogPreference

So far, you’ve seen how to use the out-of-the-box capabilities of the preferences framework, but what if you want to create a custom preference? What if you want something like the slider of the Brightness preference under Screen Settings? This is where DialogPreference comes in. DialogPreference is the parent class of EditTextPreference and ListPreference. The behavior is a dialog that pops up, displays choices to the user, and is closed with a button or via the Back button. But you can extend DialogPreference to set up your own custom preference. Within your extended class, you provide your own layout, your own click handlers, and custom code in onDialogClosed() to write the data for your preference to the shared preferences file.

Reference

Here are helpful references to topics you may wish to explore further:

Summary

This chapter talked about managing preferences in Android:

  • Types of preferences available
  • Reading the current values of preferences into your application
  • Setting default values from embedded code and by writing the default values from the XML file to the saved preferences file
  • Organizing preferences into groups, and defining dependencies between preferences
  • Callbacks on preferences to validate changes and to set dynamic summary text
  • Using the preferences framework to save and restore information from an activity across invocations
  • Creating a custom preference
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.75.59