MenuItem tinting на панели инструментов AppCompat

Когда я использую drawables из библиотеки 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" /> 

Есть ли какая-то особая магия на Toolbar AppCompat которая AppCompat только чертежи из этой библиотеки? Любой способ заставить это работать с моими собственными чертежами?

Запуск этого на устройстве уровня 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 Тонировка работает, как ожидается, с помощью AppCompat

Тонирование не работает с возможностью копирования из AppCompat Тонирование не работает с возможностью копирования из 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 }; 

    Который в значительной степени означает, что у них есть определенные атрибуты resourceIds, которые будут тонированы.

    Но я думаю, вы всегда можете увидеть, как они тонируют эти изображения и делают то же самое. Это так же просто, как установить ColorFilter на drawable.

    Установка ColorFilter (оттенка) в MenuItem проста. Вот пример:

     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); После раздувания меню и вуаля; Ваши иконки тонированы.

    После новой библиотеки поддержки 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; } 

    Я лично предпочел этот подход из этой ссылки

    Создайте 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" 

    Большинство решений в этом потоке либо используют новый 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) ); } } 

    Просто убедитесь, что вы звоните в свой код Activity / Fragment:

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

    Нет никакого отражения, нет просмотра взгляда, и не столько кода, а?

    И теперь вы можете ввернуть эти onCreateOptionsMenu/onOptionsItemSelected , потому что они полны дерьма.

    Вот решение, которое я использую; Вы можете вызвать его после 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.

    Intereting Posts
    Как фильтровать MotionEvent.getAction () на уровне API 3 (нет ACTION_MASK) База данных SQLite для Android-приложения с потенциальными несколькими пользователями Макет, отображаемый в графическом макете и на фактическом устройстве Сбой приложения при добавлении ImageView Не удалось создать интерактивное приложение на Android-устройстве: не удалось найти цель с хэш-строкой 'android-23' Не влияет ли onClickListener на пользовательский интерфейс, пока весь код внутри него не будет запущен в Android? Внедрение сканера в стиле Spring в Android Как программировать, когда FINGERPRINT_ERROR_LOCKOUT истек в Android FIngerprintManager? В Android есть Task == Application? Как изменить положение курсора в TextInput? (реагируют родной) Android – создать службу, которая работает один раз в день Ошибка Android раздувания класса SwipeRefreshLayout Как выбрать видео из галереи и получить реальный путь? Как позвонить в список контактов Android И выбрать один номер телефона на экране своих данных? Безопасен ли поток метода IntentService onStartCommand (..)?