Благодаря @JaredRummler я нашел способ определить, находится ли значок в меню переполнения или нет.Я разместил здесь полный код, в котором собраны элементы его ответа.Я также добавил вспомогательные методы для получения правильных цветов для тонирования значков.Вот что я сейчас использую:
ThemeUtils
public final class ThemeUtils {
/**
* Obtain colors of a context's theme from attributes
* @param context themed context
* @param colorAttrs varargs of color attributes
* @return array of colors in the same order as the array of attributes
*/
public static int[] getColors(Context context, int... colorAttrs) {
TypedArray ta = context.getTheme().obtainStyledAttributes(colorAttrs);
int[] colors = new int[colorAttrs.length];
for (int i = 0; i < colorAttrs.length; i++) {
colors[i] = ta.getColor(i, 0);
}
ta.recycle();
return colors;
}
/**
* Get the two colors needed for tinting toolbar icons
* The colors are obtained from the toolbar's theme and popup theme
* These themes are obtained from {@link R.attr#toolbarTheme} and {@link R.attr#toolbarPopupTheme}
* The two color attributes used are:
* - {@link android.R.attr#textColorPrimary} for the normal color
* - {@link android.R.attr#textColorSecondary} for the color in a menu
* @param context activity context
* @return int[2]{normal color, color in menu}
*/
public static int[] getToolbarColors(Context context) {
// Get the theme and popup theme of a toolbar
TypedArray ta = context.getTheme().obtainStyledAttributes(
new int[]{R.attr.toolbarTheme, R.attr.toolbarPopupTheme});
Context overlayTheme = new ContextThemeWrapper(context, ta.getResourceId(0, 0));
Context popupTheme = new ContextThemeWrapper(context, ta.getResourceId(1, 0));
ta.recycle();
// Get toolbar colors from these themes
int colorNormal = ThemeUtils.getColors(overlayTheme, android.R.attr.textColorPrimary)[0];
int colorInMenu = ThemeUtils.getColors(popupTheme, android.R.attr.textColorSecondary)[0];
return new int[]{colorNormal, colorInMenu};
}
/**
* Change the color of the icons of a menu
* Disabled items are set to 50% alpha
* @param menu targeted menu
* @param colorNormal normal icon color
* @param colorInMenu icon color for popup menu
* @param isInSubMenu whether menu is a sub menu
*/
private static void changeIconsColor(View toolbar, Menu menu, int colorNormal, int colorInMenu, boolean isInSubMenu) {
toolbar.post(() -> {
// Change icons color
for (int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
changeMenuIconColor(item, colorNormal, colorInMenu, isInSubMenu);
if (item.hasSubMenu()) {
changeIconsColor(toolbar, item.getSubMenu(), colorNormal, colorInMenu, true);
}
}
});
}
public static void changeIconsColor(View toolbar, Menu menu, int colorNormal, int colorInMenu) {
changeIconsColor(toolbar, menu, colorNormal, colorInMenu, false);
}
/**
* Change the color of a single menu item icon
* @param item targeted menu item
* @param colorNormal normal icon color
* @param colorInMenu icon color for popup menu
* @param isInSubMenu whether item is in a sub menu
*/
@SuppressLint("RestrictedApi")
public static void changeMenuIconColor(MenuItem item, int colorNormal, int colorInMenu, boolean isInSubMenu) {
if (item.getIcon() != null) {
Drawable icon = item.getIcon().mutate();
int color = (((MenuItemImpl) item).isActionButton() && !isInSubMenu ? colorNormal : colorInMenu);
icon.setColorFilter(color, PorterDuff.Mode.SRC_IN);
icon.setAlpha(item.isEnabled() ? 255 : 128);
item.setIcon(icon);
}
}
}
ActivityUtils
public final class ActivityUtils {
/**
* Force show the icons in the overflow menu and submenus
* @param menu target menu
*/
public static void forceShowMenuIcons(Menu menu) {
if (menu instanceof MenuBuilder) {
MenuBuilder m = (MenuBuilder) menu;
m.setOptionalIconsVisible(true);
}
}
/**
* Get the action bar or toolbar view in activity
* @param activity activity to get from
* @return the toolbar view
*/
public static ViewGroup findActionBar(Activity activity) {
int id = activity.getResources().getIdentifier("action_bar", "id", "android");
ViewGroup actionBar = null;
if (id != 0) {
actionBar = activity.findViewById(id);
}
if (actionBar == null) {
return findToolbar((ViewGroup) activity.findViewById(android.R.id.content).getRootView());
}
return actionBar;
}
private static ViewGroup findToolbar(ViewGroup viewGroup) {
ViewGroup toolbar = null;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View view = viewGroup.getChildAt(i);
if (view.getClass() == android.support.v7.widget.Toolbar.class ||
view.getClass() == android.widget.Toolbar.class) {
toolbar = (ViewGroup) view;
} else if (view instanceof ViewGroup) {
toolbar = findToolbar((ViewGroup) view);
}
if (toolbar != null) {
break;
}
}
return toolbar;
}
}
Я также определил два атрибута вattrs.xml
: toolbarTheme
и toolbarPopupTheme
, которые я установил на своем макете панели инструментов в XML.Их значения определены в теме моего приложения в themes.xml
.Эти атрибуты используются ThemeUtils.getToolbarColors(Context)
для получения цветов, используемых для тонирования значков, поскольку панели инструментов часто используют наложения тем.Сделав это, я могу изменить тему каждой панели инструментов, только изменив значение этих двух атрибутов.
Все, что осталось, вызывает следующее в onCreateOptionsMenu(Menu menu)
:
ActivityUtils.forceShowMenuIcons(menu); // Optional, show icons in overflow and submenus
View toolbar = ActivityUtils.findActionBar(this); // Get the action bar view
int[] toolbarColors = ThemeUtils.getToolbarColors(this); // Get the icons colors
ThemeUtils.changeIconsColor(toolbar, menu, toolbarColors[0], toolbarColors[1]);
То же самое можно сделать во фрагменте, заменив this
на getActivity()
.
. При обновлении значка MenuItem можно вызвать другой метод, ThemeUtils.changeMenuIconColor()
.В этом случае цвета панели инструментов могут быть получены в onCreate
и сохранены глобально для их повторного использования.