Settings with PreferenceFragment
Overview
In Android apps, there are often settings pages that contain different options the user can tweak. The AndroidX Preference library provides PreferenceFragmentCompat, a fragment that displays a hierarchy of preference objects on screen as a list. These preferences automatically save to SharedPreferences as the user interacts with them.
The framework’s android.preference.PreferenceFragment and the old android.support.v7.preference support-library classes are historical: the framework classes were deprecated in API 28, and the support library was superseded by AndroidX. New code should use androidx.preference classes end-to-end.
Edit your Gradle dependencies
Open your app module’s Gradle file (Your-Project/app/build.gradle) and add the AndroidX Preference library (1.2.1 is the current stable release) to the dependencies:
dependencies {
// your other dependencies...
// Kotlin projects
implementation "androidx.preference:preference-ktx:1.2.1"
// or, for Java-only projects
// implementation "androidx.preference:preference:1.2.1"
}
No theme workaround is needed: since version 1.1.0 the library falls back to the built-in PreferenceThemeOverlay style when your theme doesn’t set the preferenceTheme attribute, so the third-party “preference-v7 fix” libraries that used to patch this are obsolete.
Defining the XML
First, define the preference object hierarchy by creating a new XML file in res/xml (for example res/xml/preferences.xml):
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory app:title="@string/title_first_section">
<SwitchPreferenceCompat
app:key="switch_preference"
app:title="@string/title_switch_preference"
app:defaultValue="true" />
<EditTextPreference
app:key="edittext_preference"
app:title="@string/title_edittext_preference"
app:summary="@string/summary_edittext_preference"
app:dialogTitle="@string/dialog_title_edittext_preference"
app:dependency="switch_preference" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/title_second_section">
<ListPreference
app:key="list_preference"
app:title="@string/title_list_preference"
app:dialogTitle="@string/dialog_title_list_preference"
app:entries="@array/entries_list_preference"
app:entryValues="@array/entryvalues_list_preference" />
<Preference app:title="@string/title_intent_preference">
<intent android:action="android.intent.action.VIEW"
android:data="https://codepath.com/" />
</Preference>
</PreferenceCategory>
</PreferenceScreen>
The root for the XML file must be a <PreferenceScreen>. Within this screen, you can either list all preferences or group them with <PreferenceCategory>. The grouped preferences appear together under the same section heading. See Organize your settings for more grouping options.
All preferences are saved as key-value pairs in the default SharedPreferences with the key specified through the XML above. To retrieve an instance of those preferences, call the following with a context (note the import is androidx.preference.PreferenceManager):
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
Preference Types
There are several main types of preferences used for settings, all in the androidx.preference package:
- ListPreference: opens a dialog with a list of options (see below for defining options); persists a string
- MultiSelectListPreference: opens a dialog with checkable options; persists a string set
- EditTextPreference: opens a dialog with an EditText view for typed input; persists a string
- CheckBoxPreference: uses a checkbox to persist a boolean (true when checked, false when unchecked)
- SwitchPreferenceCompat: uses a switch to persist a boolean (true when on, false when off)
- SeekBarPreference: uses a slider to persist an int
- DropDownPreference: a
ListPreferencevariant that shows the options in a spinner-style dropdown
Preference types that open a dialog (ListPreference, MultiSelectListPreference, and EditTextPreference) descend from DialogPreference and can therefore define dialog-specific attributes in the XML (e.g. a dialogTitle).
Defining the ListPreference Options
Two arrays must be specified when defining a list preference. The first is under app:entries, which specifies human-readable options to display to the user. Second is an array of the values for each corresponding option, which is defined by app:entryValues.
<ListPreference
app:key="list_preference"
app:title="@string/title_list_preference"
app:dialogTitle="@string/dialog_title_list_preference"
app:entries="@array/entries_list_preference"
app:entryValues="@array/entryvalues_list_preference" />
Intents in Preferences
Preferences can also hold intents which can open a new activity or perform other intent actions. No data is persisted.
<Preference app:title="@string/title_intent_preference">
<intent android:action="android.intent.action.VIEW"
android:data="https://codepath.com/" />
</Preference>
Displaying the Settings Screen
Create a fragment that extends PreferenceFragmentCompat and inflate your XML hierarchy in onCreatePreferences(). To customize a preference programmatically — for example to attach an OnPreferenceChangeListener — look it up by key with findPreference() after calling setPreferencesFromResource():
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
// Indicate here the XML resource you created above that holds the preferences
setPreferencesFromResource(R.xml.preferences, rootKey)
val listPreference = findPreference<ListPreference>("list_preference")
listPreference?.setOnPreferenceChangeListener { preference, newValue ->
// your code here; return true to persist newValue
true
}
}
}
Then host the fragment from an activity (the common SettingsActivity pattern) with a regular fragment transaction:
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.settings_container, SettingsFragment())
.commit()
}
}
}
where R.id.settings_container is any container view (such as a FrameLayout) in the activity’s layout. Register the activity in your manifest as usual and launch it with an Intent from wherever your app exposes its settings entry point.
Custom Preferences
If none of the previous preference types work for your needs, you can create a custom preference extending from DialogPreference, TwoStatePreference, or Preference itself.
For a detailed guide on implementing a custom preference, refer to the Customize your settings guide.
References
- https://developer.android.com/develop/ui/views/components/settings
- https://developer.android.com/develop/ui/views/components/settings/organize-your-settings
- https://developer.android.com/develop/ui/views/components/settings/customize-your-settings
- https://developer.android.com/reference/androidx/preference/PreferenceFragmentCompat
- https://developer.android.com/jetpack/androidx/releases/preference