MenuItem在AppCompat工具栏上着色

当我从我的Toolbar菜单项AppCompat库使用drawable时,着色如预期的那样工作。 喜欢这个:

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

但是,如果我使用自己的drawable,或者甚至将AppCompat库中的drawable复制到我自己的项目中,它根本就不会着色。

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

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

abc_ic_clear_mtrl_alpha_copy是来自AppCompatabc_ic_clear_mtrl_alpha png的精确副本

编辑:

着色是基于我为我的主题android:textColorPrimary设置的值。

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

截图

着色工作如预期从AppCompat绘制 着色工作如预期从AppCompat绘制

设色不适用于从AppCompat复制的绘图 设色不适用于从AppCompat复制的绘图

因为如果你看一下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 }; 

这几乎意味着他们将特定的资源清单列入白名单。

但我想你总是可以看到他们是如何着色这些图像,并做同样的事情。 这与将ColorFilter设置为可绘制一样简单。

MenuItem上设置ColorFilter (tint)很简单。 这里是一个例子:

 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

onCreateOptionsMenu(Menu menu)调用MenuColorizer.colorMenu(this, menu, color); 充气后你的菜单和瞧; 你的图标是有色的。

在新的支持库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; } 

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

使用以下内容创build一个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"/> 

并从你的菜单中引用这个drawable:

 <item android:id="@+id/option_menu_item_something" android:icon="@drawable/ic_action_something_tined" 

这个线程中的大多数解决scheme要么使用更新的API,要么使用reflection,或者使用密集的视图查找来获取充满的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 ,因为他们充满了狗屎。

这是我使用的解决scheme; 你可以在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。