Android Setting and Preferences Activity Template in Android Studio

In this android tip, we will learn hoe to create android setting and Preferences Activity in your android application.

Setting in android can come in handy when you want your app user to select from different options based on what they do when using your app.

Applications often include settings that allow users to modify app features and behaviors. For example, some apps allow users to specify whether notifications are enabled or specify how often the application syncs data with the cloud.

A Preference object is the building block for a single setting. Each Preference appears as an item in a list and provides the appropriate UI for users to modify the setting. For example, a CheckBoxPreference creates a list item that shows a checkbox, and a ListPreference creates an item that opens a dialog with a list of choices.

Each Preference you add has a corresponding key-value pair that the system uses to save the setting in a default SharedPreferences file for your app’s settings. When the user changes a setting, the system updates the corresponding value in the SharedPreferences file for you. The only time you should directly interact with the associated SharedPreferences file is when you need to read the value in order to determine your app’s behavior based on the user’s setting.

 

List of Preferences

CheckBoxPreference – Shows an item with a checkbox for a setting that is either enabled or disabled. The saved value is a boolean (true if it’s checked).

ListPreference – Opens a dialog with a list of radio buttons. The saved value can be any one of the supported value types (listed above).

EditTextPreference – Opens a dialog with an EditText widget. The saved value is a String.

We are going to create the default android studio template for android application setting. If you have not work with android settings before, I will suggest you read Android developers manual here. You can also learn about the best design pattern for android setting and Preferences Activity here.

Before we dive into more details, it is important for us to understand what we are planning to achieve. Below is the screen-shot of the application we will be creating.

android settings

Lets start to soil our hands in code. Start up your IDE. For this tutorial, I am using the following tools and environment, feel free to use what works for you.

Windows 10

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 23

To create a new android application project, follow the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidSetting

Package: com.inducesmile.androidsetting

Select Blank Activity

Name your activity : SettingActivity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

 

Preference layout files

Once the application is created, you will notice that Android Studio has created a folder called XML inside the res folder and also four Preferences layout files.

Android setting prefers to use its preference layout from the xml folder.

 

pref_data_sync.xml – is used for the Data and sync Preference. The layout source code is shown below

android:defaultValue="180"
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
         dismiss it. -->
    <!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
    <ListPreference
        android:defaultValue="180"
        android:entries="@array/pref_sync_frequency_titles"
        android:entryValues="@array/pref_sync_frequency_values"
        android:key="sync_frequency"
        android:negativeButtonText="@null"
        android:positiveButtonText="@null"
        android:title="@string/pref_title_sync_frequency" />
    <!-- This preference simply launches an intent when selected. Use this UI sparingly, per
         design guidelines. -->
    <Preference android:title="@string/pref_title_system_sync_settings">
        <intent android:action="android.settings.SYNC_SETTINGS" />
    </Preference>
</PreferenceScreen>

pref_general.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <SwitchPreference
        android:defaultValue="true"
        android:key="example_switch"
        android:summary="@string/pref_description_social_recommendations"
        android:title="@string/pref_title_social_recommendations" />
    <!-- NOTE: EditTextPreference accepts EditText attributes. -->
    <!-- NOTE: EditTextPreference's summary should be set to its value by the activity code. -->
    <EditTextPreference
        android:capitalize="words"
        android:defaultValue="@string/pref_default_display_name"
        android:inputType="textCapWords"
        android:key="example_text"
        android:maxLines="1"
        android:selectAllOnFocus="true"
        android:singleLine="true"
        android:title="@string/pref_title_display_name" />
    <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
         dismiss it. -->
    <!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
    <ListPreference
        android:defaultValue="-1"
        android:entries="@array/pref_example_list_titles"
        android:entryValues="@array/pref_example_list_values"
        android:key="example_list"
        android:negativeButtonText="@null"
        android:positiveButtonText="@null"
        android:title="@string/pref_title_add_friends_to_messages" />
</PreferenceScreen>

pref_headers.xml

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- These settings headers are only used on tablets. -->
    <header
        android:fragment="com.inducesmile.androidsetting.SettingsActivity$GeneralPreferenceFragment"
        android:icon="@drawable/ic_info_black_24dp"
        android:title="@string/pref_header_general" />
    <header
        android:fragment="com.inducesmile.androidsetting.SettingsActivity$NotificationPreferenceFragment"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/pref_header_notifications" />
    <header
        android:fragment="com.inducesmile.androidsetting.SettingsActivity$DataSyncPreferenceFragment"
        android:icon="@drawable/ic_sync_black_24dp"
        android:title="@string/pref_header_data_sync" />
</preference-headers>

pref_notification.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- A 'parent' preference, which enables/disables child preferences (below)
         when checked/unchecked. -->
    <SwitchPreference
        android:defaultValue="true"
        android:key="notifications_new_message"
        android:title="@string/pref_title_new_message_notifications" />
    <!-- Allows the user to choose a ringtone in the 'notification' category. -->
    <!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
    <!-- NOTE: RingtonePreference's summary should be set to its value by the activity code. -->
    <RingtonePreference
        android:defaultValue="content://settings/system/notification_sound"
        android:dependency="notifications_new_message"
        android:key="notifications_new_message_ringtone"
        android:ringtoneType="notification"
        android:title="@string/pref_title_ringtone" />
    <!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
    <SwitchPreference
        android:defaultValue="true"
        android:dependency="notifications_new_message"
        android:key="notifications_new_message_vibrate"
        android:title="@string/pref_title_vibrate" />
</PreferenceScreen>

 

Strings.xml

There are other string resources that have been added in our project’s strings.xml file. Open the file to see the code below.

<resources>
    <string name="app_name">AndroidSetting</string>
    <!-- Strings related to Settings -->
    <!-- Example General settings -->
    <string name="pref_header_general">General</string>
    <string name="pref_title_social_recommendations">Enable social recommendations</string>
    <string name="pref_description_social_recommendations">Recommendations for people to contact
        based on your message history
    </string>
    <string name="pref_title_display_name">Display name</string>
    <string name="pref_default_display_name">John Smith</string>
    <string name="pref_title_add_friends_to_messages">Add friends to messages</string>
    <string-array name="pref_example_list_titles">
        <item>Always</item>
        <item>When possible</item>
        <item>Never</item>
    </string-array>
    <string-array name="pref_example_list_values">
        <item>1</item>
        <item>0</item>
        <item>-1</item>
    </string-array>
    <!-- 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 name="pref_title_system_sync_settings">System sync settings</string>
    <!-- Example settings for Notifications -->
    <string name="pref_header_notifications">Notifications</string>
    <string name="pref_title_new_message_notifications">New message notifications</string>
    <string name="pref_title_ringtone">Ringtone</string>
    <string name="pref_ringtone_silent">Silent</string>
    <string name="pref_title_vibrate">Vibrate</string>
</resources>

 

SettingsActivity.java

Now, lets consider the two other file created for us. We will start with SettingsActivity class. The code content of this class is shown below.

import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.support.v7.app.ActionBar;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.RingtonePreference;
import android.text.TextUtils;
import android.view.MenuItem;
import java.util.List;
/**
 * A {@link PreferenceActivity} that presents a set of application settings. On
 * handset devices, settings are presented as a single list. On tablets,
 * settings are split by category, with category headers shown to the left of
 * the list of settings.
 * <p>
 * See <a href="http://developer.android.com/design/patterns/settings.html">
 * Android Design: Settings</a> for design guidelines and the <a
 * href="http://developer.android.com/guide/topics/ui/settings.html">Settings
 * API Guide</a> for more information on developing a Settings UI.
 */
public class SettingsActivity extends AppCompatPreferenceActivity {
    /**
     * A preference value change listener that updates the preference's summary
     * to reflect its new value.
     */
    private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
        @Override
        public boolean onPreferenceChange(Preference preference, Object value) {
            String stringValue = value.toString();
            if (preference instanceof ListPreference) {
                // For list preferences, look up the correct display value in
                // the preference's 'entries' list.
                ListPreference listPreference = (ListPreference) preference;
                int index = listPreference.findIndexOfValue(stringValue);
                // Set the summary to reflect the new value.
                preference.setSummary(
                        index >= 0
                                ? listPreference.getEntries()[index]
                                : null);
            } else if (preference instanceof RingtonePreference) {
                // For ringtone preferences, look up the correct display value
                // using RingtoneManager.
                if (TextUtils.isEmpty(stringValue)) {
                    // Empty values correspond to 'silent' (no ringtone).
                    preference.setSummary(R.string.pref_ringtone_silent);
                } else {
                    Ringtone ringtone = RingtoneManager.getRingtone(
                            preference.getContext(), Uri.parse(stringValue));
                    if (ringtone == null) {
                        // Clear the summary if there was a lookup error.
                        preference.setSummary(null);
                    } else {
                        // Set the summary to reflect the new ringtone display
                        // name.
                        String name = ringtone.getTitle(preference.getContext());
                        preference.setSummary(name);
                    }
                }
            } else {
                // For all other preferences, set the summary to the value's
                // simple string representation.
                preference.setSummary(stringValue);
            }
            return true;
        }
    };
    /**
     * Helper method to determine if the device has an extra-large screen. For
     * example, 10" tablets are extra-large.
     */
    private static boolean isXLargeTablet(Context context) {
        return (context.getResources().getConfiguration().screenLayout
                & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
    }
    /**
     * Binds a preference's summary to its value. More specifically, when the
     * preference's value is changed, its summary (line of text below the
     * preference title) is updated to reflect the value. The summary is also
     * immediately updated upon calling this method. The exact display format is
     * dependent on the type of preference.
     *
     * @see #sBindPreferenceSummaryToValueListener
     */
    private static void bindPreferenceSummaryToValue(Preference preference) {
        // Set the listener to watch for value changes.
        preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
        // Trigger the listener immediately with the preference's
        // current value.
        sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
                PreferenceManager.getDefaultSharedPreferences(preference.getContext()).getString(preference.getKey(), ""));
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setupActionBar();
    }
    /**
     * Set up the {@link android.app.ActionBar}, if the API is available.
     */
    private void setupActionBar() {
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            // Show the Up button in the action bar.
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean onIsMultiPane() {
        return isXLargeTablet(this);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.pref_headers, target);
    }
    /**
     * This method stops fragment injection in malicious applications.
     * Make sure to deny any unknown fragments here.
     */
    protected boolean isValidFragment(String fragmentName) {
        return PreferenceFragment.class.getName().equals(fragmentName)
                || GeneralPreferenceFragment.class.getName().equals(fragmentName)
                || DataSyncPreferenceFragment.class.getName().equals(fragmentName)
                || NotificationPreferenceFragment.class.getName().equals(fragmentName);
    }
    /**
     * This fragment shows general preferences only. It is used when the
     * activity is showing a two-pane settings UI.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static class GeneralPreferenceFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.pref_general);
            setHasOptionsMenu(true);
            // Bind the summaries of EditText/List/Dialog/Ringtone preferences
            // to their values. When their values change, their summaries are
            // updated to reflect the new value, per the Android Design
            // guidelines.
            bindPreferenceSummaryToValue(findPreference("example_text"));
            bindPreferenceSummaryToValue(findPreference("example_list"));
        }
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            int id = item.getItemId();
            if (id == android.R.id.home) {
                startActivity(new Intent(getActivity(), SettingsActivity.class));
                return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
    /**
     * This fragment shows notification preferences only. It is used when the
     * activity is showing a two-pane settings UI.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static class NotificationPreferenceFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.pref_notification);
            setHasOptionsMenu(true);
            // Bind the summaries of EditText/List/Dialog/Ringtone preferences
            // to their values. When their values change, their summaries are
            // updated to reflect the new value, per the Android Design
            // guidelines.
            bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
        }
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            int id = item.getItemId();
            if (id == android.R.id.home) {
                startActivity(new Intent(getActivity(), SettingsActivity.class));
                return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
    /**
     * This fragment shows data and sync preferences only. It is used when the
     * activity is showing a two-pane settings UI.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static class DataSyncPreferenceFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.pref_data_sync);
            setHasOptionsMenu(true);
            // Bind the summaries of EditText/List/Dialog/Ringtone preferences
            // to their values. When their values change, their summaries are
            // updated to reflect the new value, per the Android Design
            // guidelines.
            bindPreferenceSummaryToValue(findPreference("sync_frequency"));
        }
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            int id = item.getItemId();
            if (id == android.R.id.home) {
                startActivity(new Intent(getActivity(), SettingsActivity.class));
                return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
}

 

AppCompatPreferenceActivity.java

AppCompatPreferenceActivity class inherits from the PreferenceActivity class which in turn inherited by the SettingsActivity class. The complete code for this class is shown below.

import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
/**
 * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
 * to be used with AppCompat.
 */
public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
    private AppCompatDelegate mDelegate;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        getDelegate().installViewFactory();
        getDelegate().onCreate(savedInstanceState);
        super.onCreate(savedInstanceState);
    }
    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        getDelegate().onPostCreate(savedInstanceState);
    }
    public ActionBar getSupportActionBar() {
        return getDelegate().getSupportActionBar();
    }
    public void setSupportActionBar(@Nullable Toolbar toolbar) {
        getDelegate().setSupportActionBar(toolbar);
    }
    @Override
    public MenuInflater getMenuInflater() {
        return getDelegate().getMenuInflater();
    }
    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }
    @Override
    public void setContentView(View view) {
        getDelegate().setContentView(view);
    }
    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().setContentView(view, params);
    }
    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().addContentView(view, params);
    }
    @Override
    protected void onPostResume() {
        super.onPostResume();
        getDelegate().onPostResume();
    }
    @Override
    protected void onTitleChanged(CharSequence title, int color) {
        super.onTitleChanged(title, color);
        getDelegate().setTitle(title);
    }
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getDelegate().onConfigurationChanged(newConfig);
    }
    @Override
    protected void onStop() {
        super.onStop();
        getDelegate().onStop();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        getDelegate().onDestroy();
    }
    public void invalidateOptionsMenu() {
        getDelegate().invalidateOptionsMenu();
    }
    private AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, null);
        }
        return mDelegate;
    }
}

This brings us to the end of this tutorial. I hope that you have learn something. Run your app to see for yourself.

You can download the code for this tutorial below. If you are having hard time downloading the tutorials, kindly contact me.

Remember to subscribe with your email address so that you will be among the first to receive my new android blog post once it is published.

Please if you love this tutorial, kindly download my android app – Complete Mathematics – in Google Play Store and let me know what you think about it.

OTHER INTERESTING POSTS:

Add a Comment