Hour 11. App Setting: Managing Preferences


What You’ll Learn in This Hour

Image Using SharedPreferences to store data

Image Setting user preferences

Image Creating a PreferencesFragment

Image Generating a PreferencesActivity


SharedPreferences(android.content.SharedPreferences) is a class that provides the ability to store data in key-value pairs. This data can be set and retrieved in any activity. Android provides a robust Preferences API for user settings that use SharedPreferences as the underlying data store. In this hour, you learn how to use SharedPreferences for a simple app setting. You’ll also develop a robust user interface for user settings in the app.

Using SharedPreferences

SharedPreferences provide a mechanism for storing and retrieving data as key-value pairs. In this hour, you create a single activity app to demonstrate how to use preferences. The Hour11App project that accompanies this hour contains this source code.

Setting Preferences

In this app, the goal is to show a message the first time the app is used. To do that, you need to know that the app has been run and you need to retain that information.

Logically, you can use a boolean value that is set to true the first time the user runs the app. After that, the value is false. Use the string "firstUse" as a key. Prefacing the key with the key with the package name for the app is common practice. If the package name is com.example.app, then the key would be "com.example.app.firstUse". That string can be stored in a values resource file or in another definition file within the app. You define the key and the possible values are true and false.

The key-value pair you use must be retained across uses of the app. The data will be put into a file that is defined with a call to the getSharedPreferences() method. This method takes two parameters, name and mode. name is the filename where the key/value pairs are stored. It is a string and should be start with the package name. The mode parameters represent the type of file storage that should be used. This is typically Context.MODE_PRIVATE indicating that the settings are private. Others options are Context.MODE_WORLD_READABLE and Context.MODE_WORLD_WRITABLE, but these values were deprecated in API level 17. In standard use, app settings and shared preferences are private, and these settings were deprecated to help enforce that privacy.

Listing 11.1 shows a snippet of code that defines string constants for the SETTINGS and FIRST_USE.SETTINGS is used as the name parameter in the getSharedPreferences() method on line 3 and FIRST_USE is used for the key value on line 5.

On line 4, a SharedPreferences.Editor is instantiated. A SharedPreferences.Editor is used to insert or update values. On line 6, commit() is called to store the values.

LISTING 11.1 Setting a Value Using SharedPreferences


 1:  public static final String SETTINGS = "com.bffmedia.hour11app.settings";
 2:  public static final String FIRST_USE = "com.bffmedia.hour11app.firstUse";
 3:  SharedPreferences preferences = getSharedPreferences(SETTINGS, MODE_PRIVATE);
 4:  Editor edit = preferences.edit();
 5:  edit.putBoolean(FIRST_USE, false);
 6:  edit.commit();


Reading from SharedPreferences

On line 3 of Listing 11.1, a SharedPreferences object called preferences was created. To read preferences, you use the appropriate get method—in this case, getBoolean(). The first parameter is the key to read and the second parameter is a default value. If the key FIRST_USE has not been set, the returned value for the boolean firstUse would be true:

boolean firstUse = preferences.getBoolean(FIRST_USE, true);

Listing 11.2 shows how the firstUse variable is read and set within an activity’s onCreate() method to display a message on the first time use of the app. On line 2, the method preferences.getBoolean() is called with the key FIRST_USE and a default setting of true. The first time this code runs, there will be no value associated with the key FIRST_USE, and the variable firstUse will be set to the default value of true. Lines 4–6 show a Toast message and lines 7–9 set the FIRST_USE value to false. This is the implementation for showing a message on the first use of the app.

LISTING 11.2 Reading and Setting Preferences


 1:  SharedPreferences preferences = getSharedPreferences(SETTINGS, MODE_PRIVATE);
 2:  boolean firstUse = preferences.getBoolean(FIRST_USE, true);
 3:  if (firstUse){
 4:  Toast helloMessage = Toast.makeText(getApplicationContext(),
 5:  "Hello First Time User",Toast.LENGTH_LONG);
 6:  helloMessage.show();
 7:  Editor editor = preferences.edit();
 8:  editor.putBoolean(FIRST_USE, false);
 9:  editor.commit();
10: }


In the case of first-time use flag, SharedPreferences serves as a storage mechanism for key-value pairs. Nothing is directly entered by the user. You can also use SharedPreferences for timestamps or for simple tracking.

You can count the number of times a user takes a certain action in your app and store the value in SharedPreferences. At a certain threshold, you might prompt the user to rate your app or take some other action.

In another example, you can use the system time to set time stamps and then check for elapsed time. Note that if a timestamp is critical to your application, then do not use the system time because it can be changed in the Settings application. The following code saves the current time in milliseconds as a long. This can be read in the future and compared to the current time as one way to determine elapsed time since the value was saved:

public static final String TIMESTAMP = "com.bffmedia.hour11app.timeStamp";
long timeStamp = System.currentTimeMillis();
SharedPreferences.Editor editor = preferences.edit();
editor.putLong(TIMESTAMP, timeStamp);
editor.commit();

Data Types and Methods in SharedPreferences

Common types can be stored and retrieved from SharedPreferences. There are methods for getBoolean(), getFloat(), getInt(), getLong(), getString(), and getStringSet().

In addition, there is a getAll() method and a contains() method. The getAll() method gets all key-value pairs as a Map where the key is a string and the value is whatever was put into SharedPreferences. The contains() method takes a string as a parameter. The string is the key to check. The contains() method returns true if the key is contained in SharedPreferences.


Note: Listening for Changes in SharedPreferences

SharedPreferences also includes a registerOnSharedPreferenceChangeListener() method. This allows your app to listen for changes to preferences and react if necessary. There is also a corresponding unregisterOnSharedPreferenceChangeListener() method.


Setting User Preferences

SharedPreferences implements a data store for key-value pairs. Android uses the SharedPreferences data store together with a set of Preference APIs to provide a robust way to implement user settings.

You can use either a PreferenceActivity(android.preference.PreferenceActivity) or a PreferenceFragment(android.preference.PreferenceFragment) as the user interface for settings. The user changes the settings, and the SharedPreferences data store updates.

The Preference class extends to provide subclasses to handle specific types of settings. Each subclass of Preference includes properties for the settings and a user interface for displaying the preference. For example, a CheckBoxPreference (android.preference.CheckBoxPreference) presents a checkbox and a message to the user. The user may check or uncheck the checkbox. The value associated with a CheckBoxPreference is a boolean with true indicating that the checkbox is checked.

Creating a PreferencesFragment

When creating a PreferenceFragment or PreferenceActivity, you use an XML file to define the preferences. This is not a res/layout XML file. Layout files define views. In this case, the XML file defines preferences. You put the preference XML file in a res/xml folder. Use the filename preferences.xml to create a PreferenceFragment.

Listing 11.3 shows the entire SettingsFragment class. SettingsFragment extends PreferenceFragment and implements onSharedPreferenceChangeListener. On line 12, within the onCreate() method, a call is made to addPreferencesFromResource(), which reads the data in the R.xml.preferences file and shows the appropriate user settings.

The onSharedPreferenceChangeListener is implemented. The registerOnSharedPreferenceChangeListener() method is called on lines 17–18 in the fragment’s onResume() method. It is unregistered in the onPause() method on lines 23–24. When a change is detected in the onSharedPreferenceChanged() method, you just log the passed key. This allows you to see when a change occurs using LogCat.

LISTING 11.3 Extending PreferencesFragment


 1:  package com.bffmedia.hour11app;
 2:  import android.content.SharedPreferences;
 3:  import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 4:  import android.os.Bundle;
 5:  import android.preference.PreferenceFragment;
 6:  import android.util.Log;
 7:  public class SettingsFragment extends PreferenceFragment
 8:  implements OnSharedPreferenceChangeListener {
 9:  @Override
10:  public void onCreate(Bundle savedInstanceState) {
11:          super.onCreate(savedInstanceState);
12:          addPreferencesFromResource(R.xml.preferences);
13:      }
14:      @Override
15:  public void onResume() {
16:          super.onResume();
17:          getPreferenceScreen().getSharedPreferences().
18:                               registerOnSharedPreferenceChangeListener(this);
19:      }
20:      @Override
21:  public void onPause() {
22:          super.onPause();
23:         getPreferenceScreen().getSharedPreferences().
24:                               unregisterOnSharedPreferenceChangeListener(this);
25:      }
26:      @Override
27:      public void onSharedPreferenceChanged(SharedPreferences
28:                                            sharedPreferences, String key) {
29:         Log.d("Settings", key);
30:      }
31:  }


The contents of the preferences.xml file determine what the SettingsFragment looks like when displayed to the user.

CheckBoxPreference

Listing 11.4 shows a sample preferences.xml file. A PreferenceScreen contains a CheckBoxPreference. Lines 4–7 include the attributes for the preference. The key on line 4 stores the associated data. The title and summary on lines 5 and 6 are used when displaying this preference. The default value on line 7 indicates the initial setting for the preference. With a default value of false, the checkbox is not enabled.

Figure 11.1 shows the SettingsFragment when the preference.xml file defined in Listing 11.4 is used.

LISTING 11.4 CheckBoxPreference Definition


 1:  <?xml version="1.0" encoding="utf-8"?>
 2:  <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
 3:     <CheckBoxPreference
 4:          android:key="hires"
 5:          android:title="Hi-Res Images"
 6:          android:summary="Show high quality images. These take longer to load"
 7:          android:defaultValue="False" />
 8:  </PreferenceScreen>


Image

FIGURE 11.1 SettingsFragment with CheckBoxPreference

In the app, the SettingsFragment is used in a SettingsActivity. The SettingsActivity just uses setContentView() to display a layout that contains the SettingsFragment. The activity_settings.xml layout file is shown here:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<fragment android:name="com.bffmedia.hour11app.SettingsFragment"
android:id="@+id/settings_fragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />
</RelativeLayout>

The onCreateMenuOptions() and onOptionsItemSelected() were implemented in the MainActivity to show Settings in the overflow menu.

Preference types including the CheckBoxPreference are listed in Table 11.1.

Image

TABLE 11.1 Preference Types

The following sections show additional simple preferences and then look at how you can handle implementing many preferences using titles and subscreens.

ListPreference

Listing 11.5 shows an additional CheckBoxPreference and a ListPreference(android.preference.ListPreference). The CheckBoxPreference indicates whether or not the user likes pie. The ListPreference lets the user indicate which type of pie he or she likes. Perhaps the app will show hi-res images of pies.

In the ListPreference definition on lines 7–15, you should note several things. Line 8 indicates a dependency on "pie", which is the CheckBoxPreference defined in lines 1–5. That means that the ListPreference will not be available if the CheckBoxPreference with the key "pie" is not selected. Figure 11.2 shows the two states for this. Line 15 specifies a defaultValue.

LISTING 11.5 ListPreference and Dependency


 1: <CheckBoxPreference
 2:       android:key="pie"
 3:       android:title="Pie"
 4:       android:summary="Like Pie"
 5:       android:defaultValue="true" />
 6:
 7: <ListPreference
 8:       android:dependency="pie"
 9:       android:key="pie_type"
 10:      android:title="Pie Type"
 11:      android:summary="Preferred pie type for eating"
 12:      android:dialogTitle="Type of Pie"
 13:      android:entries="@array/pie_array"
 14:      android:entryValues="@array/pie_array"
 15:      android:defaultValue="apple" />


Image

FIGURE 11.2 ListPreference depends on CheckBoxPreference

The ListPreference refers to the @array/pie_array that is defined in the res/values/Strings.xml file. Entries are what display to the user. EntryValues are the corresponding values. In this case, the name of the pies is both the entries and the entryValues. In other cases, the display name might be different than the entryValue. For example, a country code might be stored while a country name displays.

Figure 11.3 shows the ListPreference display.

Image

FIGURE 11.3 ListPreference displaying options

EditTextPreference

The third basic type of preference is the EditTextPreference (android.preference.EditTextPreference). The EditTextPreference is for free-form entry from the user. The following XML snippet defines an EditTextPreference. Figure 11.4 shows the resulting display: the settings page and the dialog to accept additional data.

<EditTextPreference
android:key="more_info"
android:title="More Info"
android:summary="More about pies"
android:defaultValue="" />

Image

FIGURE 11.4 EditPreference example

Adding Titles

Adding titles to organize content for settings is easy. Each set of related content that appears under a title should be wrapped in the XML in a PreferenceCategory(android.preference.PreferenceCategory) with a title. The following snippet shows a sample PreferenceCategory. The list of preferences is added to the PreferenceCategory. Figure 11.5 shows the result.

<PreferenceCategory android:title="Pie Info">

 ...add preferences here

</PreferenceCategory>

Image

FIGURE 11.5 Titles for images and pie added

This example uses titles to organize the content on the preference screen. Another way to organize the content is to use subscreens. With a subscreen, a description of the preference is shown on the main preferences page, but the user selects options on a separate subscreen.

You define subscreens by nesting a PreferenceScreen(android.preference.PreferenceScreen) element in the XML. Listing 11.4 uses one PreferenceScreen to contain all the preferences. You can add one or more additional PreferenceScreens. Listing 11.6 shows a snippet of the preferences.xml file for a subscreen. The XML shown in Listing 11.6 is nested within another PreferenceScreenElement.

Figure 11.6 shows the resulting settings page. The title appears on the main settings page. Selecting that title makes the subscreen appear.

LISTING 11.6 Showing a Subscreen


 1:  <PreferenceScreen
 2:              android:key="second_preferencescreen"
 3:              android:title="Second Screen of Settings">
 4:       <EditTextPreference
 5:          android:key="extraA"
 6:          android:title="More Data"
 7:          android:summary="Another EditTextPreference"
 8:          android:defaultValue="" />
 9:       <EditTextPreference
 10:         android:key="ExtraB"
 11:         android:title="Even More Info"
 12:         android:summary="What more can we say"
 13:         android:defaultValue="" />
 14:  </PreferenceScreen>


Image

FIGURE 11.6 Using a subscreen to organize settings

Reading Preferences

Reading these preferences is like reading from SharedPreferences, but you use PreferenceManager to get the default shared preferences:

SharedPreferences sharedPref =
PreferenceManager.getDefaultSharedPreferences(getActivity());

You can then retrieve data with gets from SharedPreferences; for example, sharedPref.getString("pie_type", "").

Generating a Preference Activity

A factor in using the PreferenceFragment is that some PreferenceActivity methods are being deprecated. Figure 11.7 shows the message from the online documentation regarding this issue.

Image

FIGURE 11.7 Deprecated methods for PreferenceActivity

On the other hand, Eclipse provides a convenient method for creating a SettingsActivity.

Figure 11.9 shows the generated SettingsActivity. You can modify it for your needs.

Image

FIGURE 11.9 The generated SettingsActivity

Summary

In this hour, you used the SharedPreference data store to save key-value pairs. In the example, you used SharedPreferences to show a message only on the first-time app use. You learned about Preferences and how to make a settings page using XML to define preferences including CheckBoxPreference, ListPreference, and EditTextPreference. You saw how to add titles and subscreens to preferences. SharedPreferences enable simple data storage, and the Preferences API is a powerful way to implement user settings

Q&A

Q. Are settings required in an app?

A. No, but using them is often a good idea. The style guide for settings provides additional information on when you should add settings. The specific guidance is: avoid the temptation to make everything a setting. (See http://developer.android.com/design/patterns/settings.html.)

Workshop

Quiz

1. What are common types of preferences?

2. What does dependency mean in a preferences file?

3. If you are retrieving a boolean value from sharedPreferences and you want the value to be false if there was no value in sharedPreferences, how would you do that?

Answers

1. Common preferences are CheckBoxPreference, ListPreference, and EditTextPreference. Table 11.1 lists additional preferences.

2. When a dependency is included for a preference, the dependent preference will not be available unless the primary preference is set. You saw this in the pie example.

3. You indicate a default value on reading the data. For example, in this statement, the default value for FIRST_USE is true: preferences.getBoolean(FIRST_USE, true);.

Exercise

Settings depend on the app you are building. Consider an app that you are building or want to build and define a list of settings. Implement those settings in either a PreferenceFragment or a PreferenceActivity.

..................Content has been hidden....................

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