9.2: Adding Settings to an App

Contents:

Apps often include settings that allow users to modify app features and behaviors. For example, some apps allow users to set their home locations, default units for measurements, dining options, and other settings that apply to the entire app. Settings are usually accessed infrequently, because once a user changes a setting, such as a home location, they rarely need to go back and change it again.

Users expect to navigate to app settings by tapping Settings in side navigation, such as a navigation drawer as shown on the left side of the figure below, or in the options menu in the app bar, shown on the right side of the figure below. Navigating to Settings from a Navigation Drawer (left) or Options Menu (right)

In the figure above:

  1. Settings in side navigation (a navigation drawer)
  2. Settings in the options menu of the app bar

In this practical you will add a settings activity to an app. Users will be able to navigate to the app settings by tapping Settings, which will be located in the options menu in the app bar.

What you should already KNOW

From the previous practicals, you should be able to:

  • Add an activity to an app.
  • Design layouts with buttons and text views.
  • Extract string resources and edit string and string array values.
  • Create an options menu in the app bar.
  • Add and edit the menu items in the options menu.
  • Add the event handler for menu item clicks.
  • Edit the AndroidManifest.xml file to add Up navigation for a second activity.
  • Read preferences from sharedPreferences.

What you will LEARN

You will learn to:

  • Add an activity and understand the use of fragments for managing settings.
  • Create an XML resource file of settings with their attributes.
  • Create navigation to the settings activity.
  • Set the default values of settings.
  • Read the settings values changed by the user.
  • Customize the Settings Activity template for your own use.

What you will DO

In this practical, you will:

  • Create an app that includes Settings in the options menu.
  • Add "Settings option" as a toggle switch.
  • Add code to set the default value for the setting, and access the setting value after it has changed.
  • Use and customize the Android Studio Settings Activity template.

App overview

Android Studio provides a shortcut for setting up an options menu with Settings. If you start an Android Studio project for a smartphone or tablet using the Basic Activity template, the new app includes Settings as shown below: App Created From the Basic Activity Template

The template also includes a floating action button in the lower right corner of the screen with an envelope icon. You can ignore this button for this practical, as you won't be using it.

You'll start by creating an app named AppWithSettings using the Basic Activity template, and add a settings activity that provides one toggle switch setting that the user can turn on or off: The Settings Activity with One Setting

You will add code to read the setting and perform an action based on its value. For the sake of simplicity, the action will be to display a toast message with the value of the setting.

In the second task, you will add the standard Settings Activity template provided by Android Studio to the DroidCafe app you created in a previous lesson. The Settings Activity template is pre-populated with settings you can customize for an app, and provides a different layout for smartphones and tablets:

  • Smartphones: A main Settings screen with a header link for each group of settings, such as General for general settings, as shown below. The Options Menu with Settings (left)

  • Tablets: A master/detail screen layout with a header link for each group on the left (master) side, and the group of settings on the right (detail) side, as shown in the figure below. General Settings on a Tablet in Master/Detail Screens

All you need to do to customize the template is change the headers, setting titles, setting descriptions, and values for the settings, and write the code you would normally write to use the values of the settings.

The Droid Cafe app was created in a previous lesson from the Basic Activity template, which provides an options menu in the app bar for placing the Settings option. You will customize the supplied Settings Activity template by changing a single setting's title, description, values, and default values. You will add code to read the setting's value after the user changes it, and display that value.

Task 1: Add a switch setting to an app

In this task, you will:

  • Create a new project based on the Basic Activity template (which provides an options menu).
  • Add a toggle switch setting with attributes in a preference XML file.
  • Add an activity for settings and a fragment for a specific setting. You will use the PreferenceFragmentCompat version of PreferenceFragment in order to maintain compatibility with AppCompatActivity. You will also add the android.support:preference-v7 library.
  • Connect the Settings item in the options menu to the settings activity.

1.1 Create the project and add the xml directory and resource file

  1. In Android Studio, create a new project with the following parameters:

    Attribute

    Value

    Application Name

    AppWithSettings

    Company Name

    android.example.com (or your own domain)

    Phone and Tablet Minimum SDK

    API15: Android 4.0.3 IceCreamSandwich

    Use a Fragment?

    Leave unchecked

    Template

    Basic Activity

  2. Run the app, and tap the overflow icon in the app bar to see the options menu, as shown in the figure below. The only item in the options menu is Settings. Tapping the Options Menu
  3. Create a new resource directory to hold the XML file containing the settings:
    1. Select the res directory in the Project: Android view, and choose File > New > Android Resource Directory. The New Resource Directory dialog appears.
    2. In the Resource type drop-down menu, choose xml. The Directory name automatically changes to xml.
    3. Click OK.
  4. The xml directory appears in the Project: Android view inside the res directory. Select the xml directory and choose File > New > XML resource file (or right-click the xml directory and choose New > XML resource file).
  5. Enter the name of the XML file, preferences, in the File name field, and click OK. The preferences.xml file appears inside the xml directory, and the layout editor appears, as shown in the figure below. Layout Editor for Preferences.xml

In the figure above:

  1. The preferences.xml file inside the xml directory.
  2. The layout editor showing the preferences.xml contents.

1.2 Add the XML preference and attributes for the setting

  1. Drag a SwitchPreference from the Palette pane on the left side to the top of the layout, as shown in the figure below. Dragging a SwitchPreference into the Layout

  2. Change the values in the Properties pane on the right side of the layout editor as follows (refer to the figure below):

    1. defaultValue: true
    2. key: example_switch
    3. title: Settings option
    4. summary: Turn this option on or off Dragging a SwitchPreference into the Layout
  3. Click the Text tab at the bottom of the layout editor to edit the XML code:

    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
       <SwitchPreference
          android:defaultValue="true"
          android:title="Settings option"
          android:key="example_switch"
          android:summary="Turn this option on or off" />
    </PreferenceScreen>
    

    The Properties values you entered represent XML attributes:

    • android:defaultValue: The default value of the setting when the app starts for the first time.
    • android:title: The title of the setting. For a SwitchPreference, the title appears to the left of the toggle switch.
    • android:key: The key to use for storing the setting value. Each setting has a corresponding key-value pair that the system uses to save the setting in a default SharedPreferences file for your app's settings.
    • android:summary: The text summary appears underneath the setting.
  4. Extract the string resources for the android:title and android:summary attribute values to @string/switch_title and @string/switch_summary.

  5. Change <SwitchPreference in the code to <android.support.v7.preference.SwitchPreferenceCompat:

    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
       <android.support.v7.preference.SwitchPreferenceCompat
          ... />
    </PreferenceScreen>
    

    In order to use the PreferenceFragmentCompat version of PreferenceFragment, you must also use the android.support.v7 version of SwitchPreference (SwitchPreferenceCompat).

    The SwitchPreferenceCompat line above may show a yellow light bulb icon with a warning, but you can ignore it.

  6. Open the styles.xml file, and add the following preferenceTheme declaration to the AppTheme:

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
       ...
       <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
    </style>
    

    In order to use the PreferenceFragmentCompat version of PreferenceFragment, you must also declare preferenceTheme with the PreferenceThemeOverlay style to the app theme.

  7. Open the build.gradle (Module: app) file, and add the following to the dependencies section:
    dependencies {
       ...
       compile 'com.android.support:preference-v7:25.0.1'
    }
    

The above adds the android.support:preference-v7 library in order to use the PreferenceFragmentCompat version of PreferenceFragment.

1.3 Add an activity for settings and a fragment for a specific setting

  1. In order to create a Settings activity that provides a UI for settings, add an Empty Activity to the app:

    1. Select app at the top of the Project: Android view.
    2. Choose New > Activity > Empty Activity.
    3. Name the activity SettingsActivity.
    4. Uncheck the option to generate a layout file (you don't need one).
    5. Leave checked the Backwards Compatibility (AppCompat) option.
    6. The Package name should already be set to com.example.android.projectname, and the Target Source Set should be set to main. If not, make these selections in the drop-down menus.
    7. Click Finish.

    The result is the following class definition in SettingsActivity:

        public class SettingsActivity extends AppCompatActivity {
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
            }
        }
    
  2. Add a blank fragment for a group of similar settings (without a layout, factory methods, or interface callbacks) to the app, in order to swap them into the Settings activity screen when needed:

    1. Select app at the top of the Project: Android view again.
    2. Choose New > Fragment > Fragment (Blank).
    3. Name the fragment SettingsFragment.
    4. Uncheck the option to generate a layout file (you don't need one).
    5. Uncheck the option to include fragment factory methods.
    6. Uncheck the option to include interface callbacks.
    7. Click Finish.

    The result is the following class definition in SettingsFragment:

        public class SettingsFragment extends Fragment {
    
            public SettingsFragment() {
                // Required empty public constructor
            }
    
            @Override
            public View onCreateView(LayoutInflater inflater,
                              ViewGroup container, Bundle savedInstanceState) {
                TextView textView = new TextView(getActivity());
                textView.setText(R.string.hello_blank_fragment);
                return textView;
            }
        }
    
  3. Change the class definition of SettingsFragment to extend PreferenceFragmentCompat:

    public class SettingsFragment extends PreferenceFragmentCompat {
    ...
    }
    

    You use a specialized Fragment subclass to display a list of settings. The best practice is to use a regular Activity that hosts a PreferenceFragment that displays the app settings. Fragments like PreferenceFragment provide a more flexible architecture for your app, compared to using activities alone. A fragment is like a modular section of an activity—it has its own lifecycle and receives its own input events, and you can add or remove a fragment while the activity is running.

    Use the PreferenceFragmentCompat version of PreferenceFragment with an activity that extends AppCompatActivity. In order to extend the fragment, you may have to add the following import statement:

    import android.support.v7.preference.PreferenceFragmentCompat;
    
  4. Replace the entire onCreateView() method in the fragment with this onCreate() method:

          @Override
          public void onCreatePreferences(Bundle savedInstanceState,
              String rootKey) {
          }
    

    The reason why you replace onCreateView() with onCreatePreferences() in SettingsFragment is because you will be adding this fragment to the existing SettingsActivity to display preferences, rather than showing a separate fragment screen. Adding it to the existing activity makes it easy to add or remove a fragment while the activity is running. The preference fragment is rooted at the PreferenceScreen using rootKey.

    You can safely remove the empty constructor from the fragment as well, since the fragment is not displayed by itself:

    public SettingsFragment() {
            // Required empty public constructor
          }
    
  5. At the end of the onCreatePreferences() method in SettingsFragment, you need to associate with this fragment the preferences.xml settings resource you just created. Add a call to setPreferencesFromResource() passing the id of the XML file (R.xml.preferences) and the rootKey to identify the preference root in PreferenceScreen:

    setPreferencesFromResource(R.xml.preferences, rootKey);
    

    The onCreatePreferences() method should now look like this:

    @Override
    public void onCreatePreferences(Bundle savedInstanceState,
                                    String rootKey) {
        setPreferencesFromResource(R.xml.preferences, rootKey);
    }
    
  6. Add the following code to the end of the SettingsActivity onCreate() method so that the fragment is displayed as the main content:

    getSupportFragmentManager().beginTransaction()
                    .replace(android.R.id.content, new SettingsFragment())
                    .commit();
    

    The above code is the typical pattern used to add a fragment to an activity so that the fragment appears as the main content of the activity. You use:

    • getFragmentManager() if the class extends Activity and the fragment extends PreferenceFragment.
    • getSupportFragmentManager() if the class extends AppCompatActivity and the fragment extends PreferenceFragmentCompat.

    The entire onCreate() method in SettingsActivity should now look like the following:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getSupportFragmentManager().beginTransaction()
                    .replace(android.R.id.content, new SettingsFragment())
                    .commit();
    }
    

1.4 Connect the Settings menu item to the settings activity

Use an intent to launch the SettingsActivity from the MainActivity.

  1. Find the if block in the onOptionsItemSelected() method in MainActivity, which handles the tap on Settings in the options menu:
    if (id == R.id.action_settings) {
       return true;
    }
    
  2. Add an intent to the if block to launch the SettingsActivity:
    if (id == R.id.action_settings) {
       Intent intent = new Intent(this, SettingsActivity.class);
       startActivity(intent);
       return true;
    }
    
  3. Add Up-button navigation to SettingsActivity by editing its declaration in the AndroidManifest.xml file to define the activity's parent as MainActivity.
    1. Find the SettingsActivity declaration in AndroidManifest.xml:
      <activity android:name=".SettingsActivity"></activity>
      
    2. Change the declaration to the following:
      <activity android:name=".SettingsActivity"
          android:label="Settings"
          android:parentActivityName=".MainActivity">
          <meta-data
              android:name="android.support.PARENT_ACTIVITY"
              android:value=".MainActivity"/>
      </activity>
      
  4. Run the app. Tap the overflow icon for the options menu (as shown on the left side of the figure below), and tap Settings to see the settings activity (as shown in the center of the figure below). Tap the Up button in the app bar of the settings activity, shown on the right side of the figure below, to return to the main activity. Tapping Settings to Navigate to the Settings Activity

1.5 Save the default values in shared preferences

Although the default value for the toggle switch setting has already been set in the android:defaultValue attribute (in Step 1.2 of this task), the app must save the default value in the SharedPreferences file for each setting when the user first opens the app. Follow these steps to set the default value for the toggle switch:

  1. In MainActivity, add the following to the end of the existing onCreate() method:

    protected void onCreate(Bundle savedInstanceState) {
       ...
       PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
    }
    

    The above code ensures that the settings are properly initialized with their default values. The setDefaultValues() method takes three arguments:

  2. The app context, such as this.
  3. The resource ID (preferences) for the XML resource file with one or more settings.
  4. A boolean indicating whether the default values should be set more than once. When false, the system sets the default values only if this method has never been called in the past. As long as you set this third argument to false, you can safely call this method every time the main activity starts without overriding the user's saved settings values. However, if you set it to true, the method will override any previous values with the defaults.

1.6 Read the changed settings value from shared preferences

When the app starts, the MainActivity's onCreate() method can read setting values that have changed, and use the changed values rather than the default values.

Each setting is identified using a key-value pair. The Android system uses this key-value pair when saving or retrieving settings from a SharedPreferences file for your app. When the user changes a setting, the system updates the corresponding value in the SharedPreferences file. To use the value of the setting, the app can use the key to get the setting from the SharedPreferences file.

Follow these steps to add that code:

  1. Before adding code to read the setting value, create a static string variable in SettingsActivity to hold the key for the value:
    public class SettingsActivity extends AppCompatActivity {
       public static final String
                       KEY_PREF_EXAMPLE_SWITCH = "example_switch";
        ...
    }
    
  2. In the onCreate() method in MainActivity, and add the following at end of the method:

    protected void onCreate(Bundle savedInstanceState) {
    ...
    SharedPreferences sharedPref =
                    PreferenceManager.getDefaultSharedPreferences(this);
    Boolean switchPref = sharedPref.getBoolean
                    (SettingsActivity.KEY_PREF_EXAMPLE_SWITCH, false);
    }
    

    The above code snippet uses

    • PreferenceManager.getDefaultSharedPreferences(this) to get the setting as a SharedPreferences object (sharedPref).
    • getBoolean() to get the Boolean value of the setting that uses the key (KEY_PREF_EXAMPLE_SWITCH defined in SettingsActivity) and assign it to switchPref. If there is no value for the key, the getBoolean() method sets the setting value (switchPref) to false. For other values such as strings, integers, or floating point numbers, you can use the getString(), getInt(), or getFloat() methods respectively.
  3. Add a Toast.makeText() method to onCreate() that displays the value of the switchPref setting in a toast:

    Toast.makeText(this, switchPref.toString(), Toast.LENGTH_SHORT).show();
    
  4. Run the app and then follow these steps:
    1. Tap Settings to see the settings activity.
    2. Tap the setting to change the toggle from on to off, as shown on the left side of the figure below.
    3. Tap the Up button in the settings activity to return to the main activity. The toast message should appear in the main activity with the value of the setting, as shown on the right side of the figure below.
    4. Repeat these steps to see the toast message change as you change the setting.

Changing a Setting and Showing its New Value

Whenever the MainActivity starts or restarts, the onCreate() method should read the setting values in order to use them in the app. The Toast.makeText() method would be replaced with a method that initializes the settings.

You now have a working settings activity in your app.

Solution code:

Android Studio project: AppWithSettings

Task 2: Using the Settings Activity template

If you need to build several sub-screens of settings and you want to take advantage of tablet-sized screens as well as maintain compatibility with older versions of Android for tablets, Android Studio provides a shortcut: the Settings Activity template.

In the previous task you learned how to use an empty settings activity and a blank fragment in order to add a setting to an app. Task 2 will now show you how to use the Settings Activity template supplied with Android Studio to:

  • Divide multiple settings into groups.
  • Customize the settings and their values.
  • Display a main Settings screen with a header link for each group of settings, such as General for general settings, as shown in the figure below. Main Settings Screen with Headers (left) and General Settings (right) on a Smartphone
  • Display a master/detail screen layout with a header link for each group on the left (master) side, and the group of settings on the right (detail) side, as shown in the figure below. General Settings on a Tablet in Master/Detail Screens

In a previous practical you created an app called Droid Cafe using the Basic Activity template, which provides an options menu in the app bar as shown below. The Options Menu in the App Bar

In the above figure:

  1. App bar.
  2. Options menu action icons.
  3. Overflow button.
  4. Options overflow menu.

Android Studio project: To start the project from where you left off in the previous practical, download the Android Studio project DroidCafe.

2.1 Explore the Settings Activity template

To include the Settings Activity template in your app project in Android Studio, follow these steps:

  1. Copy the DroidCafe project folder, rename it to DroidCafeWithSettings, and refactor it. (See the Appendixfor instructions on copying a project.) Run the app to make sure it runs properly.
  2. Select app at the top of the Project: Android view, and choose New > Activity > Settings Activity.
  3. In the dialog that appears, accept the Activity Name (SettingsActivity is the suggested name) and the Title (Settings).
  4. Click the three dots at the end of the Hierarchical Parent field and choose MainActivity as the parent activity, so that the Up button in the Settings Activity returns the user to the MainActivity. Choosing the parent activity automatically updates the AndroidManifest.xml file to support Up button navigation.
  5. Click Finish.

The Settings Activity template not only provides layouts for smartphone-sized and tablet-sized screens, but also provides the function of listening to a settings change, and changing the summary to reflect the settings change. For example, if you change the "Add friends to messages" setting (the choices are Always, When possible, or Never), the choice you make appears in the summary underneath the setting: The Setting Summary Changes with the New Value

In general, you need not change the Settings Activity template code in order to customize the activity for the settings you want in your app. You can customize the settings titles, summaries, possible values, and default values without changing the template code, and even add more settings to the groups that are provided.

You use the Settings Activity template code as-is. To make it work for your app, add code to the Main Activity to set the default settings values, and to read and use the settings values, as shown later in this task.

The Settings Activity template creates the following for you:

  • XML files in the res > xml directory, which you can add to or customize for the settings you want.

    • pref_data_sync.xml: PreferenceScreen layout for "Data & sync" settings.
    • pref_general.xml: PreferenceScreen layout for "General" settings.
    • pref_headers.xml: Layout of headers for the Settings main screen.
    • pref_notification.xml: PreferenceScreen layout for "Notifications" settings.

    The above XML layouts use various subclasses of the Preference class rather than View objects, and direct subclasses provide containers for layouts involving multiple settings. For example, PreferenceScreen represents a top-level Preference that is the root of a Preference hierarchy. The above files use PreferenceScreen at the top of each screen of settings. Other Preference subclasses for settings provide the appropriate UI for users to change the setting. For example:

    Tip: You can edit the XML files to change the default settings to settings you need for your app.

  • String resources in the strings.xml file in the res > values directory, which you can customize for the settings you want.

    All strings used in the Settings Activity, such as the titles for settings, string arrays for lists, and descriptions for settings, are defined as string resources at the end of this file. They are marked by comments such as <!-- Strings related to Settings --> and <!-- Example General settings -->.

    Tip: You can edit these strings to customize the settings you need for your app.

  • SettingsActivity in the java > com.example.android.projectname directory, which you can use as is.

    This is the activity that displays the settings. SettingsActivity extends AppCompatPreferenceActivity for maintaining compatibility with older versions of Android.

  • AppCompatPreferenceActivity in the java > com.example.android.projectname directory, which you use as is.

    This activity is a helper class that SettingsActivity uses to maintain backwards compatibility with previous versions of Android.

2.2 Add the Settings menu item and connect it to the activity

As you learned in a previous practical, you can edit the menu_main.xml file in the res > menu directory for the options menu to add or remove menu items.

  1. Edit the menu_main.xml file to add another menu item called Settings with the new resource id action_settings:

    <item
        android:id="@+id/action_settings"
        android:orderInCategory="50"
        android:title="Settings"
        app:showAsAction="never" />
    

    Specify "never" for the app:showAsAction attribute so that Settings appears only in the overflow options menu and not in the app bar itself, since it should not be used often.

    Specify "50" for the android:orderInCategory attribute so that Settings appears below Favorites (set to "40") but above Contact (set to "100"). The Options Menu with Settings

  2. Extract the string resource for "Settings" in the android:title attribute to the resource name settings.
  3. In MainActivity, find the switch-case block in the onOptionsItemSelected() method which handles the tap on items in the options menu:

    public boolean onOptionsItemSelected(MenuItem item) {
       switch (item.getItemId()) {
          case R.id.action_order:
             displayToast(getString(R.string.action_order_message));
             return true;
          case R.id.action_status:
             displayToast(getString(R.string.action_status_message));
             return true;
          case R.id.action_favorites:
                   displayToast(getString(R.string.action_favorites_message));
             return true;
          case R.id.action_contact:
             displayToast(getString(R.string.action_contact_message));
             return true;
             }
    
       return super.onOptionsItemSelected(item);
    }
    
  4. Use an intent to launch the SettingsActivity from the MainActivity. Add the intent to the end of the switch case block:

          ...
          case R.id.action_settings:
             Intent intent = new Intent(this, SettingsActivity.class);
             startActivity(intent);
             return true;
             }
    
       return super.onOptionsItemSelected(item);
    }
    
  5. Run the app using a smartphone or smartphone emulator so that you can see how the Settings Activity template handles the smartphone screen size, and follow these steps:

    1. Tap the overflow icon for the options menu, and tap Settings to see the settings activity, as shown on the left side of the figure below.

    2. Tap each setting header (General, Notifications, and Data & sync), as shown in the center of the figure below, to see the group of settings on each child screen of the Settings screen, shown on the right side of the figure below.

    3. Tap the Up button in the settings activity to return to the main activity. The Options Menu with Settings (left)

2.3 Customize the settings provided by the template

To customize the settings provided by the Settings Activity template, edit the string and string array resources in the strings.xml file and the layout attributes for each setting in the files in the xml directory. In this step you will change the "Data & sync" settings.

  1. Open the strings.xml file in the res > values directory, and scroll the contents to the <!-- Example settings for Data & Sync --> comment:

    <!-- Example settings for Data & Sync -->
        <string name="pref_header_data_sync">Data &amp; sync</string>
    
        <string name="pref_title_sync_frequency">Sync frequency</string>
        <string-array name="pref_sync_frequency_titles">
            <item>15 minutes</item>
            <item>30 minutes</item>
            <item>1 hour</item>
            <item>3 hours</item>
            <item>6 hours</item>
            <item>Never</item>
        </string-array>
        <string-array name="pref_sync_frequency_values">
            <item>15</item>
            <item>30</item>
            <item>60</item>
            <item>180</item>
            <item>360</item>
            <item>-1</item>
        </string-array>
              ...
    
  2. Edit the pref_header_data_sync string resource, which is set to Data &amp; sync(the &amp; is HTML code for an ampersand). Change the value to Account (without quotation marks).
  3. Refactor the resource name by following these steps (the app will still work without refactoring the names, but refactoring makes the code easier to understand):

    1. Control-click (or right-click) the pref_header_data_sync resource name and choose Refactor > Rename.
    2. Change the name to pref_header_account, click the option to search in comments and strings, and click Refactor.
  4. Edit the pref_title_sync_frequency string resource (which is set to Sync frequency) to Market.

  5. Refactor > Rename the resource name to pref_title_account as you did previously.
  6. Refactor > Rename the string array resource name pref_sync_frequency_titles to pref_market_titles.
  7. Change each value in the pref_market_titles string array (15 minutes, 30 minutes, 1 hour, etc.) to be the titles of markets, such as United States, Canada, etc., rather than frequencies:
    <string-array name="pref_market_titles">
            <item>United States</item>
            <item>Canada</item>
            <item>United Kingdom</item>
            <item>India</item>
            <item>Japan</item>
            <item>Other</item>
    </string-array>
    
  8. Refactor > Rename the string array resource name pref_sync_frequency_values to pref_market_values.
  9. Change each value in the pref_market_values string array (15, 30, 60, etc.) to be values for the markets—abbreviations such as US, CA, etc.:
    <string-array name="pref_market_values">
            <item>US</item>
            <item>CA</item>
            <item>UK</item>
            <item>IN</item>
            <item>JA</item>
            <item>-1</item>
        </string-array>
    
  10. Scroll down to the pref_title_system_sync_settings string resource, and edit the resource (which is set to System sync settings) to Account settings.
  11. Refactor > Rename the string array resource name pref_title_system_sync_settings to pref_title_account_settings.
  12. Open the pref_data_sync.xml file. The ListPreference in this layout defines the setting you just changed. Note that the string resources for the android:entries, android:entryValues and android:title attributes are now changed to the values you supplied in the previous steps:
    <ListPreference
            android:defaultValue="180"
            android:entries="@array/pref_market_titles"
            android:entryValues="@array/pref_market_values"
            android:key="sync_frequency"
            android:negativeButtonText="@null"
            android:positiveButtonText="@null"
            android:title="@string/pref_title_account" />
    
  13. Change the android:defaultValue attribute:
    android:defaultValue="US"
    

Since the key for this setting preference ("sync_frequency") is hard-coded elsewhere in the Java code, don't change the android:key attribute—keep using "sync_frequency" as the key for this setting in this example. If you are thoroughly customizing the settings for a real-world app, you would spend the time changing the hard-coded keys throughout the code.

Note: Why not use a string resource for the key? Because string resources can be localized for different languages using multiple-language XML files, and the key string might be inadvertently translated along with the other strings, which would cause the app to crash.

2.4 Add code to set the default values for the settings

Find the onCreate() method in MainActivity, and add the following PreferenceManager.setDefaultValues statements at the end of the method:

@Override
protected void onCreate(Bundle savedInstanceState) {
   ...
   PreferenceManager.setDefaultValues(this, R.xml.pref_general, false);
   PreferenceManager.setDefaultValues(this, R.xml.pref_notification, false);
   PreferenceManager.setDefaultValues(this, R.xml.pref_data_sync, false);
}

The default values are already specified in the XML file with the android:defaultValue attribute, but the above statements ensure that the Shared Preferences file is properly initialized with the default values. The setDefaultValues() method takes three arguments:

  • The app context, such as this.
  • The resource ID for the settings layout XML file which includes the default values set by the android:defaultValue attribute.
  • A boolean indicating whether the default values should be set more than once. When false, the system sets the default values only if this method has never been called in the past. As long as you set this third argument to false, you can safely call this method every time your activity starts without overriding the user's saved settings values by resetting them to the default values. However, if you set it to true, the method will override any previous values with the defaults.

2.5 Add code to read values for the settings

  1. Add the following code at the end of the MainActivity onCreate() method. You can add it immediately after the code you added in the previous step to set the defaults for the settings:

    ...
    SharedPreferences sharedPref =
                PreferenceManager.getDefaultSharedPreferences(this);
    String marketPref = sharedPref.getString("sync_frequency", "-1");
    Toast.makeText(this, marketPref, Toast.LENGTH_SHORT).show();
    }
    

    As you learned in the previous task, you use PreferenceManager.getDefaultSharedPreferences(this) to get the setting as a SharedPreferences object (marketPref). You then use getString() to get the string value of the setting that uses the key (sync_frequency) and assign it to marketPref. If there is no value for the key, the getString() method sets the setting value of marketPref to -1, which is the value of Other in the pref_market_values array.

  2. Run the app, again using a smartphone or smartphone emulator. When the app's main screen first appears, you see a toast message at the bottom of the screen. The first time you run the application, you should see "-1" displayed in the toast because you haven't changed the setting yet.
  3. Tap Settings in the options menu, and tap Account in the Settings screen. Choose Canada under "Market" as shown below: Changing the
  4. Tap the Up button in the app bar to return to the Settings screen, and tap it again to return to the main screen. You should see a toast message with "CA" (for Canada): Toast Showing the

    You have successfully integrated the Settings Activity with the Droid Cafe app.

  5. Now run the app on a tablet or tablet emulator. Because a tablet has a physically larger screen, the Android runtime takes advantage of the extra space. On a tablet, the settings and details are displayed on the same screen making it easier for users to manage their settings. Settings Activity on a Tablet in Horizontal Orientation

Solution code

Android Studio project: DroidCafeWithSettings (Includes coding challenge #1.)

Android Studio project: DroidCafeWithSettingsChallenge (Includes coding challenge #2.)

Coding challenges

Note: All coding challenges are optional and not prerequisite for the material in the next chapter.

Challenge 1: Add code to DroidCafeWithSettings that reads the value of the toggle switch "Enable social recommendations" on the General child screen of Settings, and displays its value along with the "Market" setting in the same toast message on the main screen.

Hint: Use a Boolean variable with shared.Pref.getBoolean and the key "example_switch".

Challenge 2: The DroidCafeWithSettings app displays the settings on a tablet-sized screen properly, but the Up button in the app bar doesn't return the user to the MainActivity as it does on a smartphone-sized screen. This is due to the onOptionsItemSelected() method in each fragment in SettingsActivity. It uses the following to restart the SettingsActivity when the user taps the Up button:

startActivity(new Intent(getActivity(), SettingsActivity.class));

The above is the appropriate action on smartphone screens in which Settings headers (General, Notifications, and Account) appear in a separate screen. After changing a setting, you want the user's tap on the Up button to take them back to the Settings headers.

However, on a tablet, the headers are always visible in the left pane (while the settings are in the right pane). As a result, tapping the Up button doesn't take the user to MainActivity.

Find a way to make the Up button work properly in SettingsActivity on tablet-sized screens.

Hint: There are several ways to fix this problem. Consider the following:

  • You can use multiple dimens.xml files in your app to accommodate different screen sizes. When the app runs on a specific device, the appropriate dimens.xml file is chosen based on the qualifiers for the dimens.xml files. For example, the app already has a dimens.xml (w820dp) file in the res > values directory, using the (w820dp) qualifier to specify a device with an 820dp screen width or larger. You can add another dimens.xml file with the Large qualifier to specify any device with a large screen, such as a tablet. The app also includes a dimens.xml file in the res > values directory for all other devices, such as smartphones.
  • You can add the following bool resource between the <resources> and </resources> tags in the dimens.xml (large) file, which is automatically chosen for tablets:
    <resources>
        <bool name="isTablet">true</bool>
    </resources>
    
  • You can add the following bool resource to the dimens.xml file, which is chosen when the app runs on any device that is not large:
    <bool name="isTablet">false</bool>
    
  • Now you can add an if-else block to the onOptionsItemSelected() method in each fragment in SettingsActivity that checks to see if isTablet is true. If it is, your code can redirect the Up button action to MainActivity.

Summary

In this practical you learned to:

  • Add a toggle switch setting (SwitchPreference) with attributes in a preference XML file, and set its attributes:
    • android:defaultValue: The setting default value.
    • android:title: The setting title.
    • android:key: The setting key.
    • android:summary: The setting summary.
  • Add a settings activity to view settings, and a fragment that extends PreferenceFragment for each specific setting.
    • Use getFragmentManager() to add the fragment to the settings activity
    • Use addPreferencesFromResource() in each fragment to load the appropriate preferences XML file for that fragment.
  • Use an intent to connect the Settings item in the options menu to the settings activity.
  • Set the default values for settings using PreferenceManager.setDefaultValues().
  • Read the settings values from SharedPreferences using PreferenceManager.getDefaultSharedPreferences(), and obtain each setting value using .getString, .getBoolean, etc.

The related concept documentation is in Android Developer Fundamentals: Concepts.

Learn more

results matching ""

    No results matching ""