MenuItem tinting on AppCompat Toolbar

前端 未结 8 1918
轮回少年
轮回少年 2020-11-28 19:26

When I use drawables from the AppCompat library for my Toolbar menu items the tinting works as expected. Like this:



        
相关标签:
8条回答
  • 2020-11-28 20:00

    After the new Support library v22.1, you can use something similar to this:

      @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;
        }
    
    0 讨论(0)
  • 2020-11-28 20:03

    Setting a ColorFilter (tint) on a MenuItem is simple. Here is an example:

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

    The above code is very helpful if you want to support different themes and you don't want to have extra copies just for the color or transparency.

    Click here for a helper class to set a ColorFilter on all the drawables in a menu, including the overflow icon.

    In onCreateOptionsMenu(Menu menu) just call MenuColorizer.colorMenu(this, menu, color); after inflating your menu and voila; your icons are tinted.

    0 讨论(0)
  • 2020-11-28 20:05

    app:iconTint attribute is implemented in SupportMenuInflater from the support library (at least in 28.0.0).

    Tested successfully with API 15 and up.

    Menu resource file:

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

    (In this case ?attr/appIconColorEnabled was a custom color attribute in the app's themes, and the icon resources were vector drawables.)

    0 讨论(0)
  • 2020-11-28 20:13

    Because if you take a look at the source code of the TintManager in AppCompat, you will see:

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

    Which pretty much means they have particular resourceIds whitelisted to be tinted.

    But I guess you can always see how they're tinting those images and do the same. It's as easy as set the ColorFilter on a drawable.

    0 讨论(0)
  • 2020-11-28 20:18

    I personally preferred this approach from this link

    Create an XML layout with the following:

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

    and reference this drawable from your menu:

    <item
        android:id="@+id/option_menu_item_something"
        android:icon="@drawable/ic_action_something_tined"
    
    0 讨论(0)
  • 2020-11-28 20:23

    Most of the solutions in this thread either use a newer API, or use reflection, or use intensive view lookup to get to the inflated MenuItem.

    However, there's a more elegant approach to do that. You need a custom Toolbar, as your "apply custom tint" use case does not play well with public styling/theming 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)
            );
        }
    
    }
    

    Just make sure you call in your Activity/Fragment code:

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

    No reflection, no view lookup, and not so much code, huh?

    And now you can ignore the ridiculous onCreateOptionsMenu/onOptionsItemSelected.

    0 讨论(0)
提交回复
热议问题