How to change Preference icon color globally Android
Asked Answered
G

4

9

I have set flat icon for all my Preference, I would like to change the color of that icon globally.

When I try the below code it even changes the back button color in the toolbar.

I want only Preference icon tint to be changed globally. Thank in advance.

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <SwitchPreference
        android:id="@+id/pref_toggle_alarm"
        android:icon="@drawable/ic_pref_notifications"
        android:key="key_toggle_alarm"
        android:summaryOff="Alarm OFF"
        android:summaryOn="Alarm ON"
        android:title="Alarm" />


    <web.prefs.TimePrefs
        android:id="@+id/pref_select_time"
        android:icon="@drawable/ic_pref_time"
        android:key="key_time"
        android:summary="Set some time"
        android:title="Select Time" />

    <MultiSelectListPreference
        android:id="@+id/pref_select_week"
        android:defaultValue="@array/week_array_values"
        android:entries="@array/array_week_selection"
        android:entryValues="@array/week_array_values"
        android:icon="@drawable/ic_pref_time"
        android:key="key_week"
        android:title="Select Days" />

    <ListPreference
        android:id="@+id/pref_track"
        android:defaultValue="0"
        android:entries="@array/tracks_arrays"
        android:entryValues="@array/tracks_arrays_values"
        android:icon="@drawable/ic_music_note"
        android:key="key_track"
        android:summary="%s"
        android:title="Select Track" />

</PreferenceScreen>

style.xml

<style name="PreferencesTheme" parent="@style/AppTheme.NoActionBar">
    <item name="android:textColorPrimary">@color/primary_text</item>
    <item name="android:textColorSecondary">@color/secondary_text</item>
    <item name="android:colorAccent">@color/accent</item>
    <item name="android:tint">@color/accent</item>
</style>
Going answered 16/3, 2017 at 3:53 Comment(1)
They change synchronously because they use the same named color, try to set different colors to the Preference items background, <item name="colorAccent">@color/your_color</item>Portwine
B
17

Here are the solutions that I found after many tests. This implies that you use at least API 21. If you are below, I recommend using a values-v21 folder and a neutral gray color which adapts to black and white backgrounds for default file.

Solution A

One solution, if you use vector icons is to make an attribute to integrate into the XML of each image.

In values/attrs.xml :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="iconPreferenceColor" format="reference|color" />
</resources>

In each icon add android:fillColor="?attr/iconPreferenceColor", sample :

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="?attr/iconPreferenceColor"
        android:pathData="M13,2.05v3.03c3.39,0.49 6,3.39 6,6.92 0,0.9 -0.18,1.75 -0.48,2.54l2.6,1.53c0.56,-1.24 0.88,-2.62 0.88,-4.07 0,-5.18 -3.95,-9.45 -9,-9.95zM12,19c-3.87,0 -7,-3.13 -7,-7 0,-3.53 2.61,-6.43 6,-6.92V2.05c-5.06,0.5 -9,4.76 -9,9.95 0,5.52 4.47,10 9.99,10 3.31,0 6.24,-1.61 8.06,-4.09l-2.6,-1.53C16.17,17.98 14.21,19 12,19z"/>
</vector>

and in style :

<item name="iconPreferenceColor">@color/green</item>

Solution B (better)

It is possible to tint the icons directly from a style file with PreferenceThemeOverlay.v14.Material.

In values/styles.xml:

<style name="MyStyle.Night" parent="Theme.AppCompat" >
    <item name="preferenceTheme">@style/MyStyle.Preference.v21</item>
    ...
</style>

<!-- Preference Theme -->
<style name="MyStyle.Preference.v21" parent="@style/PreferenceThemeOverlay.v14.Material">
    <item name="android:tint">@color/green</item>
</style>

Please note, you must also use the android:tint parameter in the style of your toolbar, otherwise you may have bugs when changing themes dynamically.

I hope it's helpful

Bucko answered 15/11, 2017 at 14:50 Comment(4)
I'd like to add: If you have a lot of icons, you can select all the xml files in drawable and refactor by variable to change all the fillColor attributes in one go.Vaillancourt
@Bucko It's better to use android:tint="?attr/iconPreferenceColor" than fillColor, which can and should stay as is.Subtilize
I noticed that setting android:tint in the preference theme overlay caused other icons in my app to also get that tint after I navigated to the preference fragment and then back. Setting <item name="android:tint">@null</item> in my AppTheme fixed it.Ruperto
It's so much easier in compose, I feel bad if I need to return to this xml approach for some reasonLumberjack
S
7

You have to change the color of preference icons programmatically, there's no way to do it by themes or XML attributes. You can add the following in your PreferenceFragment:

@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    addPreferencesFromResource(R.xml.preferences);

    int colorAttr = android.R.attr.textColorSecondary;

    TypedArray ta = context.getTheme().obtainStyledAttributes(new int[]{colorAttr});
    int iconColor = ta.getColor(0, 0);
    ta.recycle();
    tintIcons(getPreferenceScreen(), iconColor);
}

private static void tintIcons(Preference preference, int color) {
    if (preference instanceof PreferenceGroup) {
        PreferenceGroup group = ((PreferenceGroup) preference);
        for (int i = 0; i < group.getPreferenceCount(); i++) {
            tintIcons(group.getPreference(i), color);
        }
    } else {
        Drawable icon = preference.getIcon();
        if (icon != null) {
            DrawableCompat.setTint(icon, color);
        }
    }
}

Alternatively, I think this library may also be able to help you tint icons. It also fixes other issues with AppCompat preferences.

Selective answered 18/10, 2018 at 15:47 Comment(5)
When using this method, note that when the same icon is used elsewhere, it will be tinted as well.Gey
Thanks. This worked for me, although I edited the last if statement from: icon.setColorFilter(color, PorterDuff.Mode.SRC_IN); to: DrawableCompat.setTint(icon, color); As setColorFilter is now showing as deprecated. Can you elaborate on how this works? I don't understand why we seem to be setting the colour in the if and else statementsShemikashemite
@Shemikashemite A Preference can either be a group with children preferences, in which case children are tinted recursively (the if), or an actual preference with an icon, which gets tinted (the else).Selective
This is very good, especially in combination with a class derived from PreferenceFragmentCompat overriding addPreferencesFromResource(), which automatically tints the icons for descendents.Whiffet
This solution worked for me. I'm using the newer androidx framework and previous solutions didn't work. One note is that if you have a PreferenceScreen within a Group, this code will not catch it. You need to check for groups that have a preferenceCount of 0 and tint them. This will catch those that are missed by the code above.Despairing
B
0

Old question but still my answer could be valuable to someone. None of the answers above actually worked for me because I didn't want to change the whole theme but just the icon color. If you change the tint color in your reference style and put it into your main theme it will get buggy as it is said in answer above. If you just want to change the icon color and nothing, NOTHING else I highly advise using different drawable resources for day and night scenarious. In my case I have two vector drawables, one for day and one for night theme and they work just fine.

Brittan answered 22/1, 2021 at 4:32 Comment(0)
R
0

This solution does not change icons tint globally, but also does not cause issues with tint in the toolbar. Invoke this in onViewCreated() of your PreferenceFragmentCompat.

val rv = view.findViewById<RecyclerView>(androidx.preference.R.id.recycler_view)
rv?.viewTreeObserver?.addOnDrawListener {
    rv.children.forEach { pref ->
        val icon = pref.findViewById<View>(android.R.id.icon) as? PreferenceImageView
        icon?.let {
            if (it.tag != "painted") {
                it.setColorFilter(
                    ContextCompat.getColor(requireContext(), R.color.iconColor),
                    PorterDuff.Mode.SRC_IN
                )
                it.tag = "painted"
            }
        }
    }
}
Rumple answered 4/6, 2021 at 13:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.