I want to support at least api 10, I want to be able to style my preferences nicely, I want to be able to have headers (or to show PreferenceScreen
s). It seems that PreferenceActivity
, not fully supported by AppCompat
's coloring, will not fit. So I'm trying to use AppCompatActivity
and PreferenceFragmentCompat
.
public class Prefs extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) getSupportFragmentManager().beginTransaction() .replace(android.R.id.content, new PreferencesFragment()) .commit(); } public static class PreferencesFragment extends PreferenceFragmentCompat { @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); } @Override public void onDisplayPreferenceDialog(Preference preference) { // the following call results in a dialogue being shown super.onDisplayPreferenceDialog(preference); } @Override public void onNavigateToScreen(PreferenceScreen preferenceScreen) { // I can probably use this to go to to a nested preference screen // I'm not sure... } } }
Now, I want to create a custom preference that will provide the choice of a font. With PreferenceActivity
, I could simply do
import android.preference.DialogPreference; public class FontPreference extends DialogPreference { public FontPreference(Context context, AttributeSet attrs) {super(context, attrs);} @Override protected void onPrepareDialogBuilder(Builder builder) { super.onPrepareDialogBuilder(builder); // do something with builder and make a nice cute dialogue, for example, like this builder.setSingleChoiceItems(new FontAdapter(), 0, null); } }
and use xml such as this to display it
<my.app.FontPreference android:title="Choose font" android:summary="Unnecessary summary" />
But now, there is no onPrepareDialogBuilder
in android.support.v7.preference.DialogPreference
. Instead, it's been moved to PreferenceDialogFragmentCompat
. I found little information on how to use that thing, and I'm not sure how to go from xml to displaying it. v14 preference fragment has the following code:
public void onDisplayPreferenceDialog(Preference preference) { ... final DialogFragment f; if (preference instanceof EditTextPreference) f = EditTextPreferenceDialogFragment.newInstance(preference.getKey()); ... f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); }
I tried subclassing android.support.v7.preference.DialogPreference
and having onDisplayPreferenceDialog
use a similar piece of code to instantiate a dummy FontPreferenceFragment
but it fails with the following exception.
java.lang.IllegalStateException: Target fragment must implement TargetFragment interface
At this point I'm already too deep into the mess and don't want to dig further. Google knows nothing about this exception. Anyways, this method seems to be overly complicated. So, what's the best way to create custom preferences using android.support.v7.preference library?
It's still possible to customise the appearance of a Preference item though. In your XML you have to declare the root element as android:id="@android:id/widget_frame , and then declare TextView as android:title and android:summary . You can then declare other elements you want to appear in the layout.
To inflate from XML, use the addPreferencesFromResource(int) . The root element should be a PreferenceScreen . Subsequent elements can point to actual Preference subclasses. As mentioned above, subsequent PreferenceScreen in the hierarchy will result in the screen break.
Preferences in Android are used to keep track of application and user preferences. In any application, there are default preferences that can accessed through the PreferenceManager instance and its related method getDefaultSharedPreferences(Context)
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:
setTitle()
or persist*()
) can be found in the DialogPreference
class.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With