102

当我将AppCompat库中的可绘制对象用于我的Toolbar菜单项时,着色按预期工作。像这样:

<item
    android:id="@+id/action_clear"
    android:icon="@drawable/abc_ic_clear_mtrl_alpha"  <-- from AppCompat
    android:title="@string/clear" />

但是,如果我使用自己的可绘制对象,或者实际上甚至将可绘制对象从AppCompat库中复制到我自己的项目中,它根本不会着色。

<item
    android:id="@+id/action_clear"
    android:icon="@drawable/abc_ic_clear_mtrl_alpha_copy"  <-- copy from AppCompat
    android:title="@string/clear" />

AppCompat Toolbar在那个库中唯一的着色drawables中是否有一些特殊的魔法?有什么办法可以让它与我自己的可绘制对象一起使用?

compileSdkVersion = 21使用and在 API Level 19 设备上运行它targetSdkVersion = 21,并使用来自AppCompat

abc_ic_clear_mtrl_alpha_copyabc_ic_clear_mtrl_alpha来自的 png的精确副本AppCompat

编辑:

着色基于我android:textColorPrimary在主题中设置的值。

例如<item name="android:textColorPrimary">#00FF00</item>会给我一个绿色的色调。

截图

使用 AppCompat 中的 drawable 进行着色按预期工作 使用 AppCompat 中的 drawable 进行着色按预期工作

着色不适用于从 AppCompat 复制的可绘制对象 着色不适用于从 AppCompat 复制的可绘制对象

4

9 回答 9

101

在新的支持库 v22.1 之后,您可以使用类似这样的东西:

  @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_home, menu);
        Drawable drawable = menu.findItem(R.id.action_clear).getIcon();

        drawable = DrawableCompat.wrap(drawable);
        DrawableCompat.setTint(drawable, ContextCompat.getColor(this,R.color.textColorPrimary));
        menu.findItem(R.id.action_clear).setIcon(drawable);
        return true;
    }
于 2015-04-28T10:04:55.967 回答
85

在 a 上设置ColorFilter(色调)MenuItem很简单。这是一个例子:

Drawable drawable = menuItem.getIcon();
if (drawable != null) {
    // If we don't mutate the drawable, then all drawable's with this id will have a color
    // filter applied to it.
    drawable.mutate();
    drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
    drawable.setAlpha(alpha);
}

如果您想支持不同的主题并且不想仅仅为了颜色或透明度而有额外的副本,那么上面的代码非常有用。

单击此处获取帮助类以ColorFilter在菜单中的所有可绘制对象上设置 a,包括溢出图标。

在给你的菜单充气后onCreateOptionsMenu(Menu menu)打电话,瞧;MenuColorizer.colorMenu(this, menu, color);你的图标是有色的。

于 2015-01-19T09:21:34.447 回答
81

app:iconTint属性是SupportMenuInflater从支持库中实现的(至少在 28.0.0 中)。

使用 API 15 及更高版本成功测试。

菜单资源文件:

<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/menu_settings"
        android:icon="@drawable/ic_settings_white_24dp"
        app:iconTint="?attr/appIconColorEnabled"        <!-- using app name space instead of android -->
        android:menuCategory="system"
        android:orderInCategory="1"
        android:title="@string/menu_settings"
        app:showAsAction="never"
        />

    <item
        android:id="@+id/menu_themes"
        android:icon="@drawable/ic_palette_white_24dp"
        app:iconTint="?attr/appIconColorEnabled"
        android:menuCategory="system"
        android:orderInCategory="2"
        android:title="@string/menu_themes"
        app:showAsAction="never"
        />

    <item
        android:id="@+id/action_help"
        android:icon="@drawable/ic_help_white_24dp"
        app:iconTint="?attr/appIconColorEnabled"
        android:menuCategory="system"
        android:orderInCategory="3"
        android:title="@string/menu_help"
        app:showAsAction="never"
        />

</menu>

(在这种情况下?attr/appIconColorEnabled是应用程序主题中的自定义颜色属性,图标资源是矢量可绘制对象。)

于 2019-03-02T17:44:10.627 回答
31

因为如果你看一下 AppCompat 中 TintManager 的源代码,你会看到:

/**
 * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal},
 * using the default mode.
 */
private static final int[] TINT_COLOR_CONTROL_NORMAL = {
        R.drawable.abc_ic_ab_back_mtrl_am_alpha,
        R.drawable.abc_ic_go_search_api_mtrl_alpha,
        R.drawable.abc_ic_search_api_mtrl_alpha,
        R.drawable.abc_ic_commit_search_api_mtrl_alpha,
        R.drawable.abc_ic_clear_mtrl_alpha,
        R.drawable.abc_ic_menu_share_mtrl_alpha,
        R.drawable.abc_ic_menu_copy_mtrl_am_alpha,
        R.drawable.abc_ic_menu_cut_mtrl_alpha,
        R.drawable.abc_ic_menu_selectall_mtrl_alpha,
        R.drawable.abc_ic_menu_paste_mtrl_am_alpha,
        R.drawable.abc_ic_menu_moreoverflow_mtrl_alpha,
        R.drawable.abc_ic_voice_search_api_mtrl_alpha,
        R.drawable.abc_textfield_search_default_mtrl_alpha,
        R.drawable.abc_textfield_default_mtrl_alpha
};

/**
 * Drawables which should be tinted with the value of {@code R.attr.colorControlActivated},
 * using the default mode.
 */
private static final int[] TINT_COLOR_CONTROL_ACTIVATED = {
        R.drawable.abc_textfield_activated_mtrl_alpha,
        R.drawable.abc_textfield_search_activated_mtrl_alpha,
        R.drawable.abc_cab_background_top_mtrl_alpha
};

/**
 * Drawables which should be tinted with the value of {@code android.R.attr.colorBackground},
 * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode.
 */
private static final int[] TINT_COLOR_BACKGROUND_MULTIPLY = {
        R.drawable.abc_popup_background_mtrl_mult,
        R.drawable.abc_cab_background_internal_bg,
        R.drawable.abc_menu_hardkey_panel_mtrl_mult
};

/**
 * Drawables which should be tinted using a state list containing values of
 * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated}
 */
private static final int[] TINT_COLOR_CONTROL_STATE_LIST = {
        R.drawable.abc_edit_text_material,
        R.drawable.abc_tab_indicator_material,
        R.drawable.abc_textfield_search_material,
        R.drawable.abc_spinner_mtrl_am_alpha,
        R.drawable.abc_btn_check_material,
        R.drawable.abc_btn_radio_material
};

/**
 * Drawables which contain other drawables which should be tinted. The child drawable IDs
 * should be defined in one of the arrays above.
 */
private static final int[] CONTAINERS_WITH_TINT_CHILDREN = {
        R.drawable.abc_cab_background_top_material
};

这几乎意味着他们将特定的资源 ID 列入白名单以进行着色。

但我想你总能看到他们如何为这些图像着色并做同样的事情。就像在可绘制对象上设置 ColorFilter 一样简单。

于 2014-11-08T14:05:38.233 回答
30

我个人更喜欢此链接中的这种方法

使用以下内容创建 XML 布局:

<?xml version="1.0" encoding="utf-8"?>
<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/ic_action_something"
    android:tint="@color/color_action_icons_tint"/>

并从您的菜单中引用此可绘制对象:

<item
    android:id="@+id/option_menu_item_something"
    android:icon="@drawable/ic_action_something_tined"
于 2015-09-29T05:35:47.500 回答
11

该线程中的大多数解决方案要么使用更新的 API,要么使用反射,要么使用密集的视图查找来获取膨胀的MenuItem.

但是,有一种更优雅的方法可以做到这一点。您需要一个自定义工具栏,因为您的“应用自定义色调”用例不能很好地与公共样式/主题 API 配合使用。

public class MyToolbar extends Toolbar {
    ... some constructors, extracting mAccentColor from AttrSet, etc

    @Override
    public void inflateMenu(@MenuRes int resId) {
        super.inflateMenu(resId);
        Menu menu = getMenu();
        for (int i = 0; i < menu.size(); i++) {
            MenuItem item = menu.getItem(i);
            Drawable icon = item.getIcon();
            if (icon != null) {
                item.setIcon(applyTint(icon));
            }
        }
    }
    void applyTint(Drawable icon){
        icon.setColorFilter(
           new PorterDuffColorFilter(mAccentColor, PorterDuff.Mode.SRC_IN)
        );
    }

}

只要确保你调用了你的 Activity/Fragment 代码:

toolbar.inflateMenu(R.menu.some_menu);
toolbar.setOnMenuItemClickListener(someListener);

没有反射,没有视图查找,也没有那么多代码,对吧?

而现在你可以忽略荒谬了onCreateOptionsMenu/onOptionsItemSelected

于 2016-03-30T09:18:35.640 回答
8

这是我使用的解决方案;您可以在 onPrepareOptionsMenu() 或同等位置之后调用它。mutate() 的原因是如果您碰巧在多个位置使用图标;如果没有变异,它们都会呈现相同的色调。

public class MenuTintUtils {
    public static void tintAllIcons(Menu menu, final int color) {
        for (int i = 0; i < menu.size(); ++i) {
            final MenuItem item = menu.getItem(i);
            tintMenuItemIcon(color, item);
            tintShareIconIfPresent(color, item);
        }
    }

    private static void tintMenuItemIcon(int color, MenuItem item) {
        final Drawable drawable = item.getIcon();
        if (drawable != null) {
            final Drawable wrapped = DrawableCompat.wrap(drawable);
            drawable.mutate();
            DrawableCompat.setTint(wrapped, color);
            item.setIcon(drawable);
        }
    }

    private static void tintShareIconIfPresent(int color, MenuItem item) {
        if (item.getActionView() != null) {
            final View actionView = item.getActionView();
            final View expandActivitiesButton = actionView.findViewById(R.id.expand_activities_button);
            if (expandActivitiesButton != null) {
                final ImageView image = (ImageView) expandActivitiesButton.findViewById(R.id.image);
                if (image != null) {
                    final Drawable drawable = image.getDrawable();
                    final Drawable wrapped = DrawableCompat.wrap(drawable);
                    drawable.mutate();
                    DrawableCompat.setTint(wrapped, color);
                    image.setImageDrawable(drawable);
                }
            }
        }
    }
}

这不会处理溢出,但为此,您可以这样做:

布局:

<android.support.v7.widget.Toolbar
    ...
    android:theme="@style/myToolbarTheme" />

款式:

<style name="myToolbarTheme">
        <item name="colorControlNormal">#FF0000</item>
</style>

这适用于 appcompat v23.1.0。

于 2015-11-13T16:41:05.003 回答
2

这对我有用:

override fun onCreateOptionsMenu(menu: Menu?): Boolean {

        val inflater = menuInflater
        inflater.inflate(R.menu.player_menu, menu)

        //tinting menu item:
        val typedArray = theme.obtainStyledAttributes(IntArray(1) { android.R.attr.textColorSecondary })
        val textColor = typedArray.getColor(0, 0)
        typedArray.recycle()

        val item = menu?.findItem(R.id.action_chapters)
        val icon = item?.icon

        icon?.setColorFilter(textColor, PorterDuff.Mode.SRC_IN);
        item?.icon = icon
        return true
    }

或者您可以在可绘制的 xml 中使用 tint:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="?android:textColorSecondary"
    android:viewportWidth="384"
    android:viewportHeight="384">
    <path
        android:fillColor="#FF000000"

        android:pathData="M0,277.333h384v42.667h-384z" />
    <path
        android:fillColor="#FF000000"
        android:pathData="M0,170.667h384v42.667h-384z" />
    <path
        android:fillColor="#FF000000"
        android:pathData="M0,64h384v42.667h-384z" />
</vector>
于 2020-09-24T19:12:00.443 回答
0
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_home, menu);
    //One item tint
    menu.get(itemId).getIcon().setTint(Color);
   //or all
    for(int i=0;i<menu.size();i++){
    menu.get(i).getIcon().setTint(Color);
    }
    return true;
}
于 2022-01-09T17:17:45.083 回答