9.2: App settings

dewi ayu paraswati
19 min readMar 27, 2019

Task 1: Add a switch setting to an app

In this task, you do the following:

  • Create a new project based on the Basic Activity template, which provides an options menu.
  • Add a toggle switch (SwitchPreference) with attributes in a preference XML file.
  • Add an activity for settings and a fragment for a specific setting. To maintain compatibility with AppCompatActivity, you use PreferenceFragmentCompat rather than PreferenceFragment. You also add the android.support.v7.preference 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

  • In Android Studio, create a new project with the following parameters:
  • 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.
  • You need to create a new resource directory to hold the XML file containing the settings. Select the resdirectory in the Project > Android pane, and choose File > New > Android Resource Directory. The New Resource Directory dialog appears.
  • In the Resource type drop-down menu, choose xml. The Directory name automatically changes to xml. Click OK.
  • The xml folder appears in the Project > Android pane inside the res folder. Select xml and choose File > New > XML resource file (or right-click xml and choose New > XML resource file).
  • Enter the name of the XML file, preferences, in the File name field, and click OK. The preferences.xmlfile appears inside the xml folder, and the layout editor appears, as shown in the figure below.

In the figure above:

  1. The preferences.xml file inside the xmldirectory.
  2. The layout editor showing the preferences.xmlcontents.

1.2 Add the XML preference and attributes for the setting

  • Drag a SwitchPreference from the Palette pane on the left side to the top of the layout, as shown in the figure below.
  • Change the values in the Attributes pane on the right side of the layout editor as follows, and as shown in the figure below:
  • >> DefaultValue: true , key: example_switch , title: Settings option , summary: Turn this option on or off.
  • Click the Text tab at the bottom of the layout editor to see the XML code:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

<SwitchPreference
android:defaultValue="true"
android:key="example_switch"
android:summary="Turn this option on or off"
android:title="Settings option" />
</PreferenceScreen>
  • Extract the string resources for the android:titleand android:summary attribute values to @string/switch_title and @string/switch_summary.

1.3 Use SwitchPreferenceCompat

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

  • In the Project > Android pane, open the build.gradle (Module: app) file in the Gradle Scripts folder, and add the following to the dependencies section:
implementation 'com.android.support:preference-v7:26.1.0'

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

  • In the preferences.xml file in the xml folder, change <SwitchPreference in the code to <android.support.v7.preference.SwitchPreferenceCompat:
<android.support.v7.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="example_switch"
android:summary="@string/switch_summary"
android:title="@string/switch_title" />

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

  • Open the styles.xml file in the values folder, and add the following preferenceTheme item to the AppThemedeclaration:
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>

1.4 Add an Activity for settings

In order to create a settings Activity that provides a UI for settings, add an Empty Activity to the app. Follow these steps:

  • Select app at the top of the Project > Android pane, and choose New > Activity > Empty Activity.
  • Name the Activity SettingsActivity. Uncheck the Generate Layout File option (you don't need one), and leave unchecked the Launcher Activity option.
  • Leave the Backwards Compatibility (AppCompat)option checked. The Package name should already be set to com.example.android.projectname.
  • Click Finish.

1.5 Add a Fragment for a specific setting

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 Activityis running. 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. PreferenceFragment provides a more flexible architecture for your app, compared to using an Activity for the preferences.

You will use PreferenceFragmentCompat rather than PreferenceFragment in order to maintain compatibility with AppCompatActivity.

In this step you will add a blank Fragment for a group of similar settings (without a layout, factory methods, or interface callbacks) to the app, and extend PreferenceFragmentCompat.

Follow these steps:

  • Select app again, and choose New > Fragment > Fragment (Blank).
  • Name the fragment SettingsFragment. Uncheck the Create layout XML? option (you don’t need one).
  • Uncheck the options to include fragment factory methods and interface callbacks.
  • The Target Source Set should be set to main.
  • 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;
}
}
  • Edit the class definition of SettingsFragment to extend PreferenceFragmentCompat:
public class SettingsFragment extends PreferenceFragmentCompat {

As you change the class definition so it matches the definition shown above, a red bulb appears in the left margin. Click the red bulb and choose Implement methods, and then choose onCreatePreferences. Android Studio creates the following onCreatePreferences() stub:

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

In order to extend the Fragment, Android Studio adds the following import statement:

import android.support.v7.preference.PreferenceFragmentCompat;
  • Delete the entire onCreateView() method in the fragment.

The reason why you are essentially replacing onCreateView() with onCreatePreferences() is because you will be adding this SettingsFragment 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 delete the empty constructor from SettingsFragment as well, because the Fragment is not displayed by itself:

public SettingsFragment() {
// Required empty public constructor
}
  • You need to associate with this Fragment the preferences.xml settings resource you created in a previous step. Add to the newly created onCreatePreferences() stub 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);
}

1.6 Display the Fragment in SettingsActivity

To display the Fragment in SettingsActivity, follow these steps:

  • Open SettingsActivity.
  • Add the following code to the end of the onCreate() method so that the Fragment is displayed as the main content:
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();

The code above uses the typical pattern for adding a fragment to an activity so that the fragment appears as the main content of the activity:

The entire onCreate() method in SettingsActivityshould 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.7 Connect the Settings menu item to SettingsActivity

Use an Intent to launch SettingsActivity from MainActivity when the user selects Settings from the options menu.

  • Open MainActivity and find the if block in the onOptionsItemSelected() method, which handles the tap on Settings in the options menu:
if (id == R.id.action_settings) {
return true;
}
  • Add an Intent to the if block to launch SettingsActivity:
if (id == R.id.action_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
return true;
}
  • To add the app bar Up navigation button to SettingsActivity, you need to edit its declaration in the AndroidManifest.xml file to define the SettingsActivity parent as MainActivity. Open AndroidManifest.xml and find the SettingsActivity declaration:
<activity android:name=".SettingsActivity"></activity>

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>
  • Run the app. Tap the overflow icon for the options menu, as shown on the left side of the figure below. 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.

1.8 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:

  • Open MainActivity.
  • Add the following to the end of the onCreate()method after the FloatingActionButton code:
android.support.v7.preference.PreferenceManager
.setDefaultValues(this, R.xml.preferences, false);

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

  • The app context, such as this.
  • The resource ID (preferences) for the XML resource file with one or more settings.
  • 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. As long as you set this third argument to false, you can safely call this method every time MainActivity 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.9 Read the changed settings value from shared preferences

When the app starts, the MainActivity onCreate() method can read the 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 using PreferenceManager.getDefaultSharedPreferences().

Follow these steps to add that code:

  • Open SettingsActivity and create a static String variable to hold the key for the value:
public static final String 
KEY_PREF_EXAMPLE_SWITCH = "example_switch";
  • Open MainActivity and add the following at end of the onCreate() method:
SharedPreferences sharedPref =   
android.support.v7.preference.PreferenceManager
.getDefaultSharedPreferences(this);
Boolean switchPref = sharedPref.getBoolean
(SettingsActivity.KEY_PREF_EXAMPLE_SWITCH, false);
Toast.makeText(this, switchPref.toString(),
Toast.LENGTH_SHORT).show();
  • Run the app and Tap Settings to see the Settings Activity.
  • Tap the setting to change the toggle from on to off, as shown on the left side of the figure below.
  • Tap the Up button in the Settings Activity to return to MainActivity. The Toast message should appear in MainActivity with the value of the setting, as shown on the right side of the figure below.
  • Repeat these steps to see the Toast message change as you change the setting.

The code snippet shown above uses the following:

  • android.support.v7.preference.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.
  • Toast.makeText() and show() to display the value of the switchPref setting.

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.

Task 2: Use 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.

In a previous practical you created an app called DroidCafeOptionsUp using the Basic Activity template, which provides an options menu in the app bar as shown below.

Legend for the figure above:

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

2.1 Explore the Settings Activity template

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

  • Copy the DroidCafeOptionsUp project folder, and rename it to DroidCafeWithSettings. Run the app to make sure it runs properly.
  • Select app at the top of the Project > Android pane, and choose New > Activity > Settings Activity.
  • In the dialog that appears, accept the Activity Name (SettingsActivity is the suggested name) and the Title (Settings).
  • Click the three dots at the end of the Hierarchical Parent menu, and click the Project tab in the Select Activity dialog that appears (refer to the figure below).
  • Expand DroidCafeWithSettings > app > src > main > java > com.example.android.droidcafeinput and select MainActivity as the parent activity, as shown in the figure below. Click OK.

You choose MainActivity as the parent so that the Upapp bar button in the Settings Activity returns the user to the MainActivity. Choosing the parent Activityautomatically updates the AndroidManifest.xml file to support Up button navigation.

  • Click Finish.
  • In the Project > Android pane, expand the app > res > xml folder to see the XML files created by the Settings Activity template.

You can open and then add to or customize the XML files for the settings you want:

  • pref_data_sync.xml: PreferenceScreenlayout 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: PreferenceScreenlayout for "Notifications" settings.

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

The Settings Activity template also creates:

  • SettingsActivity in the java/com.example.android.projectnamefolder, 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.projectnamefolder, which you use as is. This Activity is a helper class that SettingsActivity uses to maintain backward compatibility with previous versions of Android.

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

As you learned in another practical, you can edit the menu_main.xml file for the options menu to add or remove menu items.

  • Expand the res folder in the Project > Android pane, and open menu_main.xml file. Click the Text tab to show the XML code.
  • 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" />

You specify "never" for the app:showAsActionattribute so that Settings appears only in the overflow options menu and not in the app bar itself, because it should not be used often. You specify "50" for the android:orderInCategory attribute so that Settingsappears below Favorites (set to "30") but above Contact(set to "100").

  • Extract the string resource for "Settings" in the android:title attribute to the resource name settings.
  • Open MainActivity, and find the switch case block in the onOptionsItemSelected() method which handles the tap on items in the options menu. Shown below is a snippet of that method showing the first case (for action_order):
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_order:
Intent intent = new Intent(MainActivity.this,
OrderActivity.class);
intent.putExtra(EXTRA_MESSAGE, mOrderMessage);
startActivity(intent);
return true;
case R.id.action_status:
// Code for action_status and other cases...
}

return super.onOptionsItemSelected(item);
}
  • Note in the above code that the first case uses an Intent to launch OrderActivity. Add a new case for action_settings to the switch case block with similar Intent code to launch SettingsActivity (but without the intent.putExtra):
case R.id.action_settings:
Intent settingsIntent = new Intent(this,
SettingsActivity.class);
startActivity(settingsIntent);
return true;
  • Run the app using a phone or emulator so that you can see how the Settings Activity template handles the phone screen size.
  • 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.
  • 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.
  • Tap the Up button in the Settings Activity to return to MainActivity.

You use the Settings Activity template code as-is. It not only provides layouts for phone-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:

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.

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.

  • Expand the res > values folder and open the strings.xml file. 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>

<string-array name="list_preference_entries">
<item>Entry 1</item>
<item>Entry 2</item>
<item>Entry 3</item>
</string-array>

<string-array name="list_preference_entry_values">
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>

<string-array name="multi_select_list_preference_default_value" />

<string name="pref_title_system_sync_settings">System sync settings</string>
  • 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).
  • You should now refactor the resource name (the app will still work without refactoring the name, but refactoring makes the code easier to understand). Right-click (or Control-click) the pref_header_data_sync resource name choose Refactor > Rename. Change the name to pref_header_account, click the option to search in comments and strings, and click Refactor.
  • You should also refactor the XML file name (the app will still work without refactoring the name, but refactoring makes the code easier to understand). Right-click (or Control-click) the pref_data_syncresource name in the Project > Android pane, and choose Refactor > Rename. Change the name to pref_account, click the option to search in comments and strings, and click Refactor.
  • Edit the pref_title_sync_frequency string resource (which is set to Sync frequency) to Market.
  • Refactor > Rename the pref_title_sync_frequency resource name to pref_title_account as you did previously.
  • Refactor > Rename the string array resource name pref_sync_frequency_titles to pref_market_titles.
  • Change each value in the pref_market_titlesstring 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>
  • Refactor > Rename the string array resource name pref_sync_frequency_values to pref_market_values.
  • Change each value in the pref_market_valuesstring array (15, 30, 60, etc.) to be values for the markets—abbreviations that correspond to the countries above, such as US, CA, etc.:
<string-array name="pref_market_values">
<item>US</item>
<item>CA</item>
<item>UK</item>
<item>IN</item>
<item>JA0</item>
<item>-1</item>
</string-array>
  • 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.
  • Refactor > Rename the string array resource name pref_title_system_sync_settings to pref_title_account_settings.
  • Open the pref_account.xml file. The ListPreference in this layout defines the setting you just changed. Note that the string resources for the android:entries, android:entryValuesand 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" />
  • Change the android:defaultValue attribute to "US":
android:defaultValue="US"

2.4 Add code to set the default values for the settings

To add code to set the default values for the settings, follow these steps:

  1. Open MainActivity and find the onCreate()method.
  2. Add the following PreferenceManager.setDefaultValuesstatements at the end of the onCreate() method:
PreferenceManager.setDefaultValues(this, 
R.xml.pref_general, false);
PreferenceManager.setDefaultValues(this,
R.xml.pref_notification, false);
PreferenceManager.setDefaultValues(this,
R.xml.pref_account, false);

The default values are already specified in the XML file with the android:defaultValue attribute, but the above statements ensure that the SharedPreferences 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 when this method is called for the first time. 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

  • 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");
displayToast(marketPref);

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 assigns the setting value of marketPref to -1, which is the value of Other in the pref_market_values array.

  • Run the app. 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 app, you should see "-1" displayed in the Toast because you haven't changed the setting yet.
  • Tap Settings in the options menu, and tap Account in the Settings screen. Tap Market, and choose Canadaas shown below:
  • Tap the Up button in the app bar to return to the Settings screen, and tap it again to return to the main screen.
  • Run the app again from Android Studio. You should see a Toast message with "CA" (for Canada), and the Market setting is now set to Canada.

You have successfully integrated the Settings Activity with the app.

  • 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.

--

--