如何全局更改Android偏好设置图标颜色

9
我已经为所有我的偏好设置设置了扁平图标,我想要全局更改该图标的颜色。
当我尝试下面的代码时,它甚至会改变工具栏中的返回按钮颜色。
我只想全局更改Preference图标的色调。提前感谢您的帮助。
<?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>

它们同步更改,因为它们使用相同命名的颜色,请尝试将不同的颜色设置为首选项项目的背景,<item name="colorAccent">@color/your_color</item> - Yu Jiaao
4个回答

17

在经过多次测试后,我找到了以下解决方案。这意味着您至少使用API 21。如果您的API版本低于21,我建议使用values-v21文件夹和一个适应黑白背景的中性灰色作为默认文件。

解决方案A

如果您使用矢量图标,则一种解决方案是创建一个属性并将其集成到每个图像的XML中。

在values/attrs.xml中:

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

在每个图标中添加 android:fillColor="?attr/iconPreferenceColor",示例:

<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>

并且以样式呈现:

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

解决方案 B(更好)

可以使用PreferenceThemeOverlay.v14.Material从样式文件直接对图标进行着色。

在 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>
请注意,您还必须在工具栏的样式中使用android:tint参数,否则在动态更改主题时可能会出现错误。

希望有所帮助。


我想补充一点:如果你有很多图标,你可以选择 drawable 中的所有 xml 文件,并通过变量重构来一次性更改所有 fillColor 属性。 - Bryan W
@J-Jamet 最好使用 android:tint="?attr/iconPreferenceColor" 而不是 fillColor,fillColor 可以保持原样。 - Lior Iluz
3
我注意到在首选项主题叠加中设置android:tint会导致在我导航到首选项片段然后返回之后,应用程序中的其他图标也会获得该色调。在我的AppTheme中设置<item name="android:tint">@null</item>可以解决这个问题。 - omiwrench
在Compose中更容易得多,如果因为某种原因需要返回到这种XML方法,我会感到很抱歉。 - brucemax

7

您需要通过编程来更改偏好设置图标的颜色,无法通过主题或XML属性进行更改。您可以在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);
        }
    }
}

另外,我认为这个库也可能有助于您对图标进行着色。它还可以修复AppCompat偏好设置的其他问题。该库

使用此方法时,请注意,当相同的图标在其他地方使用时,它也会被着色。 - mattlaabs
谢谢。这对我有用,尽管我编辑了最后一个if语句,从: icon.setColorFilter(color, PorterDuff.Mode.SRC_IN); 到: DrawableCompat.setTint(icon, color); 因为setColorFilter现在显示为已弃用。你能详细说明一下这是如何工作的吗?我不明白为什么我们似乎要在if和else语句中设置颜色。 - pledgeX
1
@pledgeX Preference 可以是一个带有子偏好设置的组,此时子偏好设置会递归着色(if 部分),或者是一个具有图标的实际偏好设置,该图标会被着色(else 部分)。 - Nicolas
这非常好,尤其是与从PreferenceFragmentCompat派生的类相结合,覆盖addPreferencesFromResource()方法,可以自动为子项着色图标。 - chksr
这个解决方案对我很有效。我正在使用更新的androidx框架,之前的解决方案不起作用。需要注意的是,如果你在一个组内有一个PreferenceScreen,这段代码将无法捕获它。你需要检查那些具有preferenceCount为0的组并对它们进行着色。这将捕获上面代码所忽略的那些内容。 - Mark

0

虽然这是一个老问题,但我的答案仍然对某些人有价值。 以上所有答案都没有真正为我工作,因为我不想改变整个主题,而只是想改变图标颜色。 如果您更改参考样式中的色调颜色并将其放入主题中,它将会出现错误,就像上面的答案所说。 如果您只想更改图标颜色,而不是任何其他东西,我强烈建议为白天和黑夜场景使用不同的可绘制资源。 在我的情况下,我有两个矢量可绘制对象,一个用于白天,一个用于夜间主题,它们运行得非常好。


0
该解决方案不会全局更改图标色调,但也不会影响工具栏中的色调。在PreferenceFragmentCompat的onViewCreated()方法中调用它即可。
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"
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接