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()
orpersist*()
) can be found in theDialogPreference
class. - All dialog (-editing) methods (
onBindDialogView(View)
&onDialogClosed(boolean)
) have been moved toPreferenceDialogFragmentCompat
.
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
orListPreference
, 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 thisDialogPreference
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 inonBindDialogView()
.In your preference screen which extends PreferenceFragmentCompat, override
onDisplayPreferenceDialog()
to launch your customPreferenceDialogFragmentCompat
.You must only extend the support classes, not the platform classes. For example, extend
androidx.preference.EditTextPreference
instead ofandroid.preference.EditTextPreference