How do I create custom preferences using android.support.v7.preference library?

Important note: Currently (v23.0.1 of the v7 library) there are still a lot of theme-issues with the 'PreferenceThemeOverlay'(see this issue). On Lollipop for example, you end up with Holo-styled category headers.

After some frustrating hours, I finally succeeded to create a custom v7 Preference. Creating your own Preference appears to be harder than you might think is needed. So make sure to take some time.

At first you might be wondering why you will find both a DialogPreference and a PreferenceDialogFragmentCompat for each preference type. As it turns out, the first one is the actual preference, the second is the DialogFragment where the preference would be displayed in. Sadly, you are required to subclass both of them.

Don't worry, you won't need to change any piece of code. You only need to relocate some methods:

  • All preference-editing methods (like setTitle() or persist*()) can be found in the DialogPreference class.
  • All dialog (-editing) methods (onBindDialogView(View) & onDialogClosed(boolean)) have been moved to PreferenceDialogFragmentCompat.

You might want your existing class to extend the first one, that way you don't have to change to much I think. Autocomplete should help you find missing methods.

When you have completed the above steps, it is time to bind these two classes together. In your xml file, you will refer to the preference-part. However, Android doesn't know yet which Fragment it must inflate when your custom preference needs to be. Therefore, you need to override onDisplayPreferenceDialog(Preference):

@Override
public void onDisplayPreferenceDialog(Preference preference) {
    DialogFragment fragment;
    if (preference instanceof LocationChooserDialog) {
        fragment = LocationChooserFragmentCompat.newInstance(preference);
        fragment.setTargetFragment(this, 0);
        fragment.show(getFragmentManager(),
                "android.support.v7.preference.PreferenceFragment.DIALOG");
    } else super.onDisplayPreferenceDialog(preference);
}

and also your DialogFragment needs to handle the 'key':

public static YourPreferenceDialogFragmentCompat newInstance(Preference preference) {
    YourPreferenceDialogFragmentCompat fragment = new YourPreferenceDialogFragmentCompat();
    Bundle bundle = new Bundle(1);
    bundle.putString("key", preference.getKey());
    fragment.setArguments(bundle);
    return fragment;
}

That should do the trick. If you encounter problems, try taking a look at existing subclasses and see how Android solved it (in Android Studio: type a class' name and press Ctrl+b to see the decompiled class). Hope it helps.


There is a good tutorial and Github project that explains in detail how to make a custom preference class that extends the Support Preference library:

  • https://medium.com/@JakobUlbrich/building-a-settings-screen-for-android-part-3-ae9793fd31ec -- "Building an Android Settings Screen (Part 3)", by Jakob Ulbrich

  • https://github.com/jakobulbrich/preferences-demo -- Sample Android project on Github

The key points are:

  • You will need a custom DialogPreference or ListPreference, which controls how the preference row looks and functions. (It can also contain a reference to a layout that should display in the launched dialog). Add this DialogPreference to your XML preference file.

  • You will need a custom PreferenceDialogFragmentCompat, which controls the launching of the Dialog when the preference row is clicked. You can configure the Dialog's view in onBindDialogView().

  • In your preference screen which extends PreferenceFragmentCompat, override onDisplayPreferenceDialog() to launch your custom PreferenceDialogFragmentCompat.

  • You must only extend the support classes, not the platform classes. For example, extend androidx.preference.EditTextPreference instead of android.preference.EditTextPreference