Teñido de MenuItem en la barra de herramientas de AppCompat

Cuando uso los elementos extraíbles de la biblioteca de AppCompat para los elementos del menú de la Toolbar , el tinte funciona como se esperaba. Me gusta esto:

 <item android:id="@+id/action_clear" android:icon="@drawable/abc_ic_clear_mtrl_alpha"  

Pero si utilizo mis propios elementos extraíbles o incluso copio los elementos extraíbles de la biblioteca de AppCompat en mi propio proyecto, no se tiñerá en absoluto.

 <item android:id="@+id/action_clear" android:icon="@drawable/abc_ic_clear_mtrl_alpha_copy"  

¿Hay alguna magia especial en la AppCompat Toolbar AppCompat que solo AppCompat los AppCompat extraíbles de esa biblioteca? ¿Hay alguna manera de hacer que esto funcione con mis propios objetos descartables?

Ejecutando esto en el dispositivo API Nivel 19 con compileSdkVersion = 21 y targetSdkVersion = 21 , y también usando todo desde AppCompat

abc_ic_clear_mtrl_alpha_copy es una copia exacta de abc_ic_clear_mtrl_alpha png de AppCompat

Editar:

El tinte se basa en el valor que he establecido para android:textColorPrimary en mi tema.

Por ejemplo, #00FF00 me daría un color de tinte verde.

Capturas de pantalla

Tinte de trabajo como se esperaba con drawable de AppCompat Tinte de trabajo como se esperaba con drawable de AppCompat

Tinte no funciona con Drawable copiado de AppCompat Tinte no funciona con Drawable copiado de AppCompat

    Porque si echas un vistazo al código fuente de TintManager en AppCompat, verás:

     /** * 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 }; 

    Lo que significa que tienen ID de recursos particulares en la lista blanca para ser tintados.

    Pero supongo que siempre se puede ver cómo están polarizando esas imágenes y hacer lo mismo. Es tan fácil como configurar el ColorFilter en un dibujo.

    Establecer un ColorFilter (tinte) en un MenuItem es simple. Aquí hay un ejemplo:

     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); } 

    El código anterior es muy útil si desea admitir diferentes temas y no desea tener copias adicionales solo por el color o la transparencia.

    Haga clic aquí para ver una clase de ayudante para establecer un ColorFilter en todos los ColorFilter de un menú, incluido el icono de desbordamiento.

    En onCreateOptionsMenu(Menu menu) simplemente llame a MenuColorizer.colorMenu(this, menu, color); después de inflar tu menú y listo; tus íconos están teñidos

    Después de la nueva biblioteca de soporte v22.1, puede usar algo similar a esto:

      @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; } 

    Personalmente prefiero este enfoque desde este enlace

    Crea un diseño XML con lo siguiente:

     < ?xml version="1.0" encoding="utf-8"?>  

    y referencia este dibujable de tu menú:

      

    La mayoría de las soluciones en este subproceso usan una API más nueva, o usan reflexión, o usan una búsqueda intensiva de vista para llegar al elemento de MenuItem inflado.

    Sin embargo, hay un enfoque más elegante para hacer eso. Necesita una barra de herramientas personalizada, ya que su caso de uso de “aplicar tinte personalizado” no funciona bien con la API de estilo / tematización pública.

     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) ); } } 

    Solo asegúrate de llamar tu código de Actividad / Fragmento:

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

    Sin reflexión, sin búsqueda de vistas, y no tanto código, ¿eh?

    Y ahora puedes ignorar el ridículo onCreateOptionsMenu/onOptionsItemSelected .

    Aquí está la solución que uso; puede llamarlo después de onPrepareOptionsMenu () o el lugar equivalente. El motivo de mutate () es si usa los íconos en más de una ubicación; sin el mutado, todos tomarán el mismo tono.

     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); } } } } } 

    Esto no se ocupará del desbordamiento, pero para eso, puede hacer esto:

    Diseño:

      

    Estilos:

      

    Esto funciona a partir de appcompat v23.1.0.