Тонировка меню на панели инструментов AppCompat


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

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

запуск это на уровне API 19 устройство с compileSdkVersion = 21 и targetSdkVersion = 21, а также используя все от AppCompat

abc_ic_clear_mtrl_alpha_copy - это точная копия!--11--> png из AppCompat

Edit:

тонировка основана на значении, которое я установил для android:textColorPrimary в моей теме.

например.<item name="android:textColorPrimary">#00FF00</item> дал бы мне зеленый оттенок цвета.

скриншоты

тонировка работает, как и ожидалось с drawable от Совместимости приложений

тонировка не совместим с катры скопировал от совместимости приложений

6 74

6 ответов:

потому что если вы посмотрите на исходный код 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"/>

и ссылаться на этот drawable из вашего меню:

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

}

просто убедитесь, что вы вызываете свой код активности / фрагмента:

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

нет отражения, нет просмотра, и не так много Код, да?

и теперь вы можете игнорировать смешно onCreateOptionsMenu/onOptionsItemSelected.

вот решение, которое я использую; вы можете назвать его после onPrepareOptionsMenu() или эквивалентное место. Причина мутации () заключается в том, что вы используете значки в нескольких местах; без мутации все они будут иметь один и тот же оттенок.

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.