Przyciemnianie menu na pasku narzędzi AppCompat

Kiedy używam drawables z biblioteki AppCompat dla moich pozycji menu Toolbar, przyciemnianie działa zgodnie z oczekiwaniami. Tak:

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

Ale jeśli użyję własnych rysunków lub nawet skopiuję je z biblioteki AppCompat do własnego projektu, to w ogóle nie będzie to odcień.

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

Czy jest jakaś specjalna magia w AppCompat Toolbar to tylko odcień rysunków z tej biblioteki? Jakiś sposób, żeby to zadziałało z moimi własnymi rysunkami?

Uruchamianie tego na urządzeniu poziomu API 19 z compileSdkVersion = 21 i targetSdkVersion = 21, a także używając wszystkiego z AppCompat

abc_ic_clear_mtrl_alpha_copy jest dokładną kopią abc_ic_clear_mtrl_alpha png z AppCompat

Edit:

Przyciemnianie jest oparte na wartości, którą ustawiłem dla android:textColorPrimary w moim temacie.

Np. <item name="android:textColorPrimary">#00FF00</item> dałby mi zielony odcień.

Zrzuty ekranu

Przyciemnianie działa zgodnie z oczekiwaniami przy użyciu drawable from AppCompat Przyciemnianie działa zgodnie z oczekiwaniami dzięki funkcji drawable from AppCompat

Przyciemnianie nie działa z drawable copied from AppCompat Przyciemnianie nie działa z rysowalnym kopiowaniem z AppCompat

Author: greve, 2014-11-06

6 answers

Ponieważ jeśli spojrzysz na kod źródłowy Menedżera TintManager w AppCompat, zobaczysz:

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

Co w zasadzie oznacza, że mają konkretne zasoby do przyciemnienia.

Ale myślę, że zawsze można zobaczyć, jak przyciemniają te obrazy i zrobić to samo. To tak proste, jak ustawić filtr kolorów na rysunku.

 31
Author: EvilDuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-08 14:05:38

Ustawienie a ColorFilter (tint) na a MenuItem jest proste. Oto przykład:

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

Powyższy kod jest bardzo pomocny, jeśli chcesz obsługiwać różne motywy i nie chcesz mieć dodatkowych kopii tylko dla koloru lub przezroczystości.

Kliknij tutaj aby Klasa pomocnicza ustawiała ColorFilter na wszystkich rysunkach w menu, łącznie z ikoną przepełnienia.

W onCreateOptionsMenu(Menu menu) wystarczy zadzwonić MenuColorizer.colorMenu(this, menu, color); po napompowaniu menu i voila; ikony są przyciemnione.

 75
Author: Jared Rummler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-19 09:21:34

Po nowej bibliotece wsparcia v22. 1, możesz użyć czegoś podobnego do tego:

  @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;
    }
 73
Author: Mahdi Hijazi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-14 06:13:25

Osobiście wolałem takie podejście od tego linku

Utwórz układ XML o następującej treści:

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

I odwołaj się do tego z menu:

<item
    android:id="@+id/option_menu_item_something"
    android:icon="@drawable/ic_action_something_tined"
 24
Author: N Jay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-29 06:40:13

Większość rozwiązań w tym wątku albo używa nowszego API, albo używa reflection, albo używa intensywnego wyszukiwania widoku, aby dostać się do napompowanego MenuItem.

Jest jednak bardziej eleganckie podejście do tego. Potrzebujesz niestandardowego paska narzędzi, ponieważ twój przypadek użycia "Zastosuj Niestandardowy odcień" nie gra dobrze z publicznym interfejsem API stylizacji/motywów.

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

}

Po prostu upewnij się, że zadzwonisz do swojej aktywności/kodu fragmentu:

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

Brak refleksji, brak wyszukiwania widoku, i nie tyle Kod, co?

And now you można zignorować Śmieszne onCreateOptionsMenu/onOptionsItemSelected.

 9
Author: Drew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-27 14:12:28

Oto rozwiązanie, które używam; można wywołać go po onprepareoptionsmenu() lub równoważne miejsce. Powodem mutate () jest użycie ikon w więcej niż jednym miejscu; bez mutate wszystkie przybierają ten sam odcień.

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

To nie zajmie się przepełnieniem, ale za to możesz to zrobić:

Układ:

<android.support.v7.widget.Toolbar
    ...
    android:theme="@style/myToolbarTheme" />

Style:

<style name="myToolbarTheme">
        <item name="colorControlNormal">#FF0000</item>
</style>

To działa od appcompat v23. 1. 0.

 7
Author: Learn OpenGL ES,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-13 16:41:05