Kiedy używam rysunków z AppCompat
biblioteki dla moich Toolbar
pozycji menu, zabarwienie działa zgodnie z oczekiwaniami. Lubię to:
<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ę rysunki z AppCompat
biblioteki do własnego projektu, to w ogóle nie zabarwi się .
<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ś szczególna magia w AppCompat
Toolbar
tym jedynym odcieniu rysunków z tej biblioteki? Czy jest jakiś sposób, aby to działało z moimi własnymi rysunkami?
Uruchomienie tego na urządzeniu z interfejsem API Level 19 z compileSdkVersion = 21
i targetSdkVersion = 21
, a także przy użyciu wszystkiego zAppCompat
abc_ic_clear_mtrl_alpha_copy
jest dokładną kopią pliku abc_ic_clear_mtrl_alpha
png zAppCompat
Edytować:
Zabarwienie jest oparte na wartości, którą ustawiłem android:textColorPrimary
w moim motywie.
Np. <item name="android:textColorPrimary">#00FF00</item>
Dałoby mi zielony odcień.
Zrzuty ekranu
Barwienie działa zgodnie z oczekiwaniami dzięki możliwości rysowania z AppCompat
Barwienie nie działa z rysunkiem skopiowanym z AppCompat
Odpowiedzi:
Ponieważ jeśli spojrzysz na kod źródłowy TintManagera 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łaściwie oznacza, że mają określone identyfikatory zasobów na białej liście do przyciemnienia.
Ale myślę, że zawsze możesz zobaczyć, jak zabarwiają te obrazy i zrobić to samo. To tak proste, jak ustawienie ColorFilter na rysowalnym.
źródło
Po nowej bibliotece pomocy technicznej w wersji 22.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; }
źródło
setColorFilter()
jest o wiele lepsze.Ustawienie
ColorFilter
(odcień) na aMenuItem
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 a
ColorFilter
na wszystkich elementach do rysowania w menu, w tym na ikonie przepełnienia.W
onCreateOptionsMenu(Menu menu)
wystarczy zadzwonićMenuColorizer.colorMenu(this, menu, color);
po pompowania swoje menu i voila; Twoje ikony są zabarwione.źródło
app:iconTint
atrybut jest zaimplementowanySupportMenuInflater
z biblioteki obsługi (przynajmniej w 28.0.0).Przetestowane pomyślnie z API 15 i nowszymi.
Plik zasobów 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>
(W tym przypadku
?attr/appIconColorEnabled
był to niestandardowy atrybut koloru w motywach aplikacji, a zasoby ikon były rysunkami wektorowymi).źródło
android:iconTint
iandroid:iconTintMode
nie działa, ale prefiksowanieapp:
zamiastandroid:
działa jak urok (na moich własnych rysunkach wektorowych, API> = 21)SupportMenuInflater
nie zastosuje żadnej niestandardowej logiki, jeśli menu nie jestSupportMenu
podobneMenuBuilder
, po prostu wróci do zwykłegoMenuInflater
.AppCompatActivity.startSupportActionMode(callback)
i odpowiednie implementacje wsparciaandroidx.appcompat
.Osobiście wolałem to podejście z tego linku
Utwórz układ XML z następującymi elementami:
<?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, co można wyciągnąć z menu:
<item android:id="@+id/option_menu_item_something" android:icon="@drawable/ic_action_something_tined"
źródło
bitmap
. Istnieją inne sposoby kolorowania wektorów. Może ktoś mógłby tu też dodać kolorowanie wektorowe ...Większość rozwiązań w tym wątku używa nowszego interfejsu API, używa odbicia lub intensywnego wyszukiwania widoku, aby uzyskać zawyżone
MenuItem
.Istnieje jednak bardziej eleganckie podejście do tego. Potrzebujesz niestandardowego paska narzędzi Toolbar, ponieważ Twój przypadek użycia „Zastosuj niestandardowy odcień” nie współ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 wywołujesz kod aktywności / fragmentu:
toolbar.inflateMenu(R.menu.some_menu); toolbar.setOnMenuItemClickListener(someListener);
Bez odbicia, bez podglądu i nie tyle kodu, co?
A teraz możesz zignorować śmieszne
onCreateOptionsMenu/onOptionsItemSelected
.źródło
Menu#getItem()
złożoność paska narzędzi wynosi O (1), ponieważ elementy są przechowywane w ArrayList. Co różni się odView#findViewById
traversal (które w mojej odpowiedzi określiłem jako przeglądanie widoku ), którego złożoność jest daleka od stałej :-)Oto rozwiązanie, którego używam; możesz go wywołać po onPrepareOptionsMenu () lub równoważnym miejscu. Powodem mutate () jest używanie ikon w więcej niż jednym miejscu; bez mutatu wszystkie nabiorą tego samego odcienia.
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 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>
Działa to od wersji 23.1.0 appcompat.
źródło
To zadziałało dla mnie:
override fun onCreateOptionsMenu(menu: Menu?): Boolean { val inflater = menuInflater inflater.inflate(R.menu.player_menu, menu) //tinting menu item: val typedArray = theme.obtainStyledAttributes(IntArray(1) { android.R.attr.textColorSecondary }) val textColor = typedArray.getColor(0, 0) typedArray.recycle() val item = menu?.findItem(R.id.action_chapters) val icon = item?.icon icon?.setColorFilter(textColor, PorterDuff.Mode.SRC_IN); item?.icon = icon return true }
Lub możesz użyć tinta w formacie XML do rysowania:
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:tint="?android:textColorSecondary" android:viewportWidth="384" android:viewportHeight="384"> <path android:fillColor="#FF000000" android:pathData="M0,277.333h384v42.667h-384z" /> <path android:fillColor="#FF000000" android:pathData="M0,170.667h384v42.667h-384z" /> <path android:fillColor="#FF000000" android:pathData="M0,64h384v42.667h-384z" /> </vector>
źródło