Lorsque j'utilise des éléments dessinables de la bibliothèque AppCompat
pour mes éléments de menu Toolbar
, la teinte fonctionne comme prévu. Comme ça:
<item
Android:id="@+id/action_clear"
Android:icon="@drawable/abc_ic_clear_mtrl_alpha" <-- from AppCompat
Android:title="@string/clear" />
Mais si j'utilise mes propres dessinables ou si je les copie de la bibliothèque AppCompat
dans mon propre projet, cela ne se colorera pas du tout.
<item
Android:id="@+id/action_clear"
Android:icon="@drawable/abc_ic_clear_mtrl_alpha_copy" <-- copy from AppCompat
Android:title="@string/clear" />
Existe-t-il une magie spéciale dans le AppCompat
Toolbar
qui ne te permet de dessiner que les tirages de cette bibliothèque? Un moyen d'obtenir que cela fonctionne avec mes propres dessinables?
Exécuter ceci sur un périphérique API de niveau 19 avec compileSdkVersion = 21
et targetSdkVersion = 21
, et aussi en utilisant tout de AppCompat
abc_ic_clear_mtrl_alpha_copy
est une copie exacte de la abc_ic_clear_mtrl_alpha
_ png de AppCompat
Edit:
La teinte est basée sur la valeur que j'ai définie pour Android:textColorPrimary
dans mon thème.
Par exemple. <item name="Android:textColorPrimary">#00FF00</item>
me donnerait une couleur de teinte verte.
Captures d'écran
La teinte fonctionne comme prévu avec la possibilité d'être dessiné à partir d'AppCompat
La teinte ne fonctionne pas avec la copie imprimable à partir d'AppCompat
Parce que si vous regardez le code source du TintManager dans AppCompat, vous verrez:
/**
* 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
};
Ce qui signifie quasiment qu'ils ont des ressources particulières dans leur liste blanche à teinter.
Mais je suppose que vous pouvez toujours voir comment ils colorent ces images et font de même. C'est aussi simple que de régler le ColorFilter sur un dessinable.
Après la nouvelle bibliothèque de support v22.1, vous pouvez utiliser quelque chose de similaire à ceci:
@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;
}
Définir un ColorFilter
(teinte) sur un MenuItem
est simple. Voici un exemple:
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);
}
Le code ci-dessus est très utile si vous souhaitez prendre en charge différents thèmes et que vous ne voulez pas de copies supplémentaires uniquement pour la couleur ou la transparence.
Cliquez ici pour qu'une classe d'assistance définisse un ColorFilter
sur tous les éléments dessinables d'un menu, y compris le icône de débordement.
Dans onCreateOptionsMenu(Menu menu)
, appelez simplement MenuColorizer.colorMenu(this, menu, color);
après avoir gonflé votre menu et le tour est joué; vos icônes sont teintées.
Personnellement, j'ai préféré cette approche à partir de cette lien
Créez une mise en page XML avec les éléments suivants:
<?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"/>
et référencez-le dans votre menu:
<item
Android:id="@+id/option_menu_item_something"
Android:icon="@drawable/ic_action_something_tined"
L'attribut app:iconTint
Est implémenté dans SupportMenuInflater
à partir de la bibliothèque de support (au moins dans 28.0.0).
Testé avec succès avec API 15 et plus.
Fichier de ressources de menu:
<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>
(Dans ce cas, ?attr/appIconColorEnabled
Était un attribut de couleur personnalisé dans les thèmes de l'application et les ressources d'icône étaient des vecteurs dessiables.)
La plupart des solutions de ce fil de discussion utilisent soit une API plus récente, soit une réflexion, soit une recherche intensive dans les vues pour accéder à la variable MenuItem
gonflée.
Cependant, il existe une approche plus élégante pour le faire. Vous avez besoin d'une barre d'outils personnalisée, car votre cas d'utilisation "appliquer une teinte personnalisée" ne fonctionne pas bien avec l'API de styling/theming publique.
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)
);
}
}
Assurez-vous simplement que vous appelez votre code d'activité/fragment:
toolbar.inflateMenu(R.menu.some_menu);
toolbar.setOnMenuItemClickListener(someListener);
Pas de réflexion, pas de recherche de vue, et pas tellement de code, hein?
Et maintenant, vous pouvez ignorer le ridicule onCreateOptionsMenu/onOptionsItemSelected
.
Voici la solution que j'utilise; vous pouvez l'appeler après onPrepareOptionsMenu () ou son emplacement équivalent. La raison de la mutation () est si vous utilisez les icônes à plusieurs endroits; sans la mutation, ils prendront tous la même teinte.
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);
}
}
}
}
}
Cela ne résoudra pas le problème de débordement, mais pour cela, vous pouvez le faire:
Disposition:
<Android.support.v7.widget.Toolbar
...
Android:theme="@style/myToolbarTheme" />
Modes:
<style name="myToolbarTheme">
<item name="colorControlNormal">#FF0000</item>
</style>
Cela fonctionne à partir de appcompat v23.1.0.