Когда я использую чертежи из AppCompat
библиотеки для Toolbar
элементов меню, тонировка работает должным образом. Как это:
<item
android:id="@+id/action_clear"
android:icon="@drawable/abc_ic_clear_mtrl_alpha" <-- from AppCompat
android:title="@string/clear" />
Но если я использую свои собственные чертежи или даже копирую чертежи из AppCompat
библиотеки в свой собственный проект, он вообще не будет окрашен.
<item
android:id="@+id/action_clear"
android:icon="@drawable/abc_ic_clear_mtrl_alpha_copy" <-- copy from AppCompat
android:title="@string/clear" />
Есть ли какая-то особая магия в том, AppCompat
Toolbar
что только оттенки из этой библиотеки? Есть ли способ заставить это работать с моими собственными чертежами?
Запуск этого на устройстве уровня API 19 с помощью compileSdkVersion = 21
и targetSdkVersion = 21
, а также использование всего изAppCompat
abc_ic_clear_mtrl_alpha_copy
является точной копией abc_ic_clear_mtrl_alpha
png изAppCompat
Редактировать:
Тонировка основана на значении, которое я установил android:textColorPrimary
в своей теме.
Например <item name="android:textColorPrimary">#00FF00</item>
, дал бы мне зеленый оттенок.
Скриншоты
Тонирование работает должным образом с возможностью вывода из AppCompat
Тонирование не работает с drawable, скопированным из AppCompat
Ответы:
Потому что, если вы посмотрите исходный код TintManager в AppCompat, вы увидите:
/** * 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 };
Что в значительной степени означает, что у них есть определенные идентификаторы ресурсов в белом списке для тонирования.
Но я думаю, вы всегда можете увидеть, как они окрашивают эти изображения и делают то же самое. Это так же просто, как установить ColorFilter на чертеже.
источник
После новой библиотеки поддержки v22.1 вы можете использовать что-то подобное:
@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; }
источник
setColorFilter()
предпочтительнее.Установить
ColorFilter
(оттенок) на aMenuItem
просто. Вот пример: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); }
Приведенный выше код очень полезен, если вы хотите поддерживать разные темы и не хотите иметь дополнительные копии только для цвета или прозрачности.
Щелкните здесь, чтобы получить вспомогательный класс, чтобы установить его
ColorFilter
для всех элементов меню, включая значок переполнения.В
onCreateOptionsMenu(Menu menu)
только вызовеMenuColorizer.colorMenu(this, menu, color);
после накачивания вашего меню и вуаля; ваши значки тонированы.источник
app:iconTint
атрибут реализованSupportMenuInflater
из библиотеки поддержки (по крайней мере, в 28.0.0).Успешно протестирован с API 15 и выше.
Файл ресурсов меню:
<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>
(В данном случае это
?attr/appIconColorEnabled
был настраиваемый атрибут цвета в темах приложения, а ресурсы значков были векторными рисунками.)источник
android:iconTint
иandroid:iconTintMode
не работайте, но префикс сapp:
вместоandroid:
работает как шарм (на моих собственных векторных чертежах, API> = 21)SupportMenuInflater
никакая пользовательская логика не применяется, если меню неSupportMenu
похожеMenuBuilder
, оно просто возвращается к обычномуMenuInflater
.AppCompatActivity.startSupportActionMode(callback)
и соответствующие реализации поддержки изandroidx.appcompat
будут переданы в обратный вызов.Я лично предпочел этот подход из этой ссылки
Создайте макет XML со следующим:
<?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"/>
и ссылайтесь на этот чертеж из своего меню:
<item android:id="@+id/option_menu_item_something" android:icon="@drawable/ic_action_something_tined"
источник
bitmap
. Есть и другие способы раскрашивания векторов. Возможно, кто-нибудь сможет добавить сюда векторную раскраску ...Большинство решений в этом потоке либо используют более новый API, либо используют отражение, либо используют интенсивный поиск в представлениях, чтобы добраться до завышенного
MenuItem
.Однако для этого есть более элегантный подход. Вам нужна настраиваемая панель инструментов, так как ваш вариант использования «применить настраиваемый оттенок» плохо сочетается с общедоступным 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) ); } }
Просто убедитесь, что вы вызываете свой код активности / фрагмента:
toolbar.inflateMenu(R.menu.some_menu); toolbar.setOnMenuItemClickListener(someListener);
Без отражения, без просмотра и не так много кода, да?
А теперь можно игнорировать нелепое
onCreateOptionsMenu/onOptionsItemSelected
.источник
Menu#getItem()
сложность на панели инструментов равна O (1), потому что элементы хранятся в ArrayList. Это отличается отView#findViewById
обхода (который я назвал в своем ответе поиском представления ), сложность которого далеко не постоянна :-)Вот решение, которое я использую; вы можете вызвать его после onPrepareOptionsMenu () или аналогичного места. Причина использования mutate () заключается в том, что вы используете значки более чем в одном месте; без мутации все они приобретут одинаковый оттенок.
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); } } } } }
Это не позаботится о переполнении, но для этого вы можете сделать это:
Макет:
<android.support.v7.widget.Toolbar ... android:theme="@style/myToolbarTheme" />
Стили:
<style name="myToolbarTheme"> <item name="colorControlNormal">#FF0000</item> </style>
Это работает с appcompat v23.1.0.
источник
Это сработало для меня:
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 }
Или вы можете использовать оттенок в drawable xml:
<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>
источник