I have been wrestling with trying to get my PreferenceFragment
to have the same Material-based theme and styling (via AppCompat) as the rest of my application. The PreferenceFragment
that I am using to manage all of my application settings is shown below:
As you can see from the screenshot above, I was able to customize the PreferenceFragment
by using colorAccent
, colorPrimary
, and a few other attributes. My theme for the PreferenceFragment
is as follows:
<style
name="settingsTheme"
parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/blue_grey_500</item>
<item name="colorAccent">@color/blue_grey_500</item>
<item name="android:textColor">@color/black_text_alt</item>
<item name="android:textColorSecondary">@color/black_secondary_text</item>
</style>
However, despite my best efforts, I am unable to apply themes to the individual elements within my PreferenceFragment
, such as ListPreference
and EditTextPreference
; all of the preferences still retain the standard AppCompat theme:
I found an 2 year old post that discusses this issue, albeit with no real solution: How to apply theme to <PreferenceScreen> elements of a <PreferenceCategory>
I am wondering if anyone has been able to successfully apply themes to the preferences since AppComat V21 has been released. If not, are there any viable workarounds that can be used to apply custom themes to individual preferences?
UPDATE: I made a library to battle the issue (uses AppCompat r22): https://github.com/consp1racy/android-support-preference
The ListPreference
extends DialogPreference
which uses this piece of code to create the dialog:
mBuilder = new AlertDialog.Builder(context)
.setTitle(mDialogTitle)
.setIcon(mDialogIcon)
.setPositiveButton(mPositiveButtonText, this)
.setNegativeButton(mNegativeButtonText, this);
As you can see the AlertDialog.Builder
constructor is not supplied with the second optional int theme
parameter. That means the dialog will be themed by whatever your activity's theme has in its android:alertDialogTheme
attribute.
Now you have to create a custom theme for your dialog which derives from Theme.AppCompat.Dialog
like so:
<style name="Theme.YourApp.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="colorAccent">@color/accent_yourapp</item>
<item name="colorPrimary">@color/primary_yourapp</item>
<item name="colorPrimaryDark">@color/primary_dark_yourapp</item>
</style>
Problem 1: The above solution will not work for RingtonePreference
because it does not extend ListPreference
but calls a ringtone chooser intent, so it's always themed according to system. Check out this answer:
RingtonePreference Theme So we can mark this as solved.
Problem 2: The AppCompat dialogs lack title. And so far I haven't found a way to fix this. True that I am not looking hard enough. Let's ignore the title absence as a minor issue.
Problem 3: The radio button drawables are not mutated so the graphics are inconsistent between passive and active (colored) state - all are colored (not just the one you!re pressing) or all are grey. Now this is really annoying
Problems 2 & 3 forced me to take another route - my dialog theme looks like this on API 14+
<style name="Theme.MyApp.Dialog.Alert" parent="android:Theme.DeviceDefault.Light.Dialog.MinWidth">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
</style>
and like this on API 21+
<style name="Theme.YourApp.Dialog.Alert" parent="android:Theme.DeviceDefault.Light.Dialog.MinWidth">
<item name="android:colorPrimary">@color/primary_yourapp</item>
<item name="android:colorPrimaryDark">@color/primary_dark_yourapp</item>
<item name="android:colorAccent">@color/accent_yourapp</item>
</style>
These values have been experimentally gained by crawling through the platform's source files and well tested.
The point is that the only reliable solution seems to be using the device default dialog theme. The only choice before Lollipop is a light or dark variant. On Lollipop this will work as intended and requested.
EDIT: Since appcompat-v7-r21.1.0 you can use AppCompatDialog
which is material themed variant of native AlertDialog
.
You can use provided AlertDialog.Builder
(not to be confused with its native counterpart) for creating its instances.
DialogPreference
uses android.app.AlertDialog.Builder
directly so it would be impossible to switch the displayed dialog to the material design one for all API levels (which requires using android.support.v7.widget.AlertDialog.Builder
).
I believe the only option for now is to extend and override dialog creation logic in DialogPreference
or its subclasses (and use reflection to work around private accessors if required). Check out this reddit thread and an example AppCompatListPreference
made by the author of that thread.
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