Тонировка значков панели инструментов на Android


Я заметил, что при использовании тем AppCompat значки панели инструментов по умолчанию окрашиваются атрибутом colorControlNormal в моем стиле.

<style name="MyTheme" parent="Theme.AppCompat">
    <item name="colorControlNormal">@color/yellow</item>
</style>

Введите описание изображения здесь

Однако, как вы можете видеть выше, это происходит не со всеми иконами. Я предоставил знак "плюс", который я получил от официальных иконок, и он не тонируется (я использовал" белую " версию png). Из того, что я понял из этого вопроса, система подкрашивает только значки только с альфа-каналом. Это правда?

Если да : есть ли место, где я могу найти Альфа-определенные, официальные материальные значки? Если нет - и если значки панели инструментов должны быть только альфа-тонированными - как Google ожидает, что мы будем использовать предоставленные значки в панели инструментов?

Где-то в SDK я нашел несколько иконок, заканчивающихся на _alpha.png, и они действительно хорошо тонируются. Однако мне нужен полный набор материальных иконок, а из официальных источников я смог найти только white, grey600 и black единицы.

Применение a ColorFilter при время выполнения будет немного болезненным, и моя фактическая панель инструментов - с некоторыми тонированными значками, некоторые другие нет - выглядит довольно плохо.

6 26

6 ответов:

Здесь решение, которое я использую. Вызовите tintAllIcons после onPrepareOptionsMenu или эквивалентного местоположения. Причина mutate() заключается в том, что вы используете значки в нескольких местах; без 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>

Это работает в совместимости приложений v23.1.0.

Другой вариант-использовать новую поддержку векторных чертежей в библиотеке поддержки.

Смотрите res/xml/ic_search.xml в блоге AppCompat-возраст векторов

Обратите внимание на ссылку на ?attr/colorControlNormal

<vector xmlns:android="..."
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0"
    android:tint="?attr/colorControlNormal">
    <path
        android:pathData="..."
        android:fillColor="@android:color/white"/>
</vector>

Я действительно смог сделать это на API 10 (Gingerbread), и это сработало очень хорошо.

Edit : Он также работал на API 22...

Вот окончательный результат.

Введите описание изображения здесь

Примечание: значок-это ресурс с возможностью рисования в папке (папках) с возможностью рисования.

Теперь вот как это делается:

@Override
public void onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);

    MenuItem item = menu.findItem(R.id.action_refresh);
    Drawable icon = getResources().getDrawable(R.drawable.ic_refresh_white_24dp);
    icon.setColorFilter(getResources().getColor(R.color.colorAccent), PorterDuff.Mode.SRC_IN);

    item.setIcon(icon);
}

В этот момент Вы можете изменить его на любой цвет, который вы хотите!

Я вижу, что этот вопрос получает некоторые мнения, поэтому я собираюсь опубликовать ответ для тех, кто не читает комментарии.

Все мои предположения в этом вопросе были ошибочными, и дело не в Альфа-каналах, по крайней мере, не во внешнем мире. Дело просто в том, что, цитируя @alanv,

AppCompat только подкрашивает свои собственные иконки. На данный момент вам нужно будет вручную подкрашивайте любые значки, которые вы предоставляете отдельно от AppCompat.

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

Лично я использую colorControlNormal, который является черным или белым (или подобными оттенками), и импортирую значки с этим конкретным цветом. Цветные значки на цветном фоне выглядят немного плохо. Однако другое решение, которое я нашел приятным, - это этот класс на github. Ты просто позвони MenuColorizer.colorMenu() при создании меню.

Вы можете просто создать пользовательскую панель инструментов, которая использует ваш оттенок цвета при раздувании меню.

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, Если вы используете этот подход

@NonNull
public static Drawable setTintDrawable(@NonNull Drawable drawable, @ColorInt int color) {
   drawable.clearColorFilter();
   drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
   drawable.invalidateSelf();
   Drawable wrapDrawable = DrawableCompat.wrap(drawable).mutate();
   DrawableCompat.setTint(wrapDrawable, color);
   return wrapDrawable;
 }

И вызовите таким образом:

MenuItem location = menu.findItem(R.id.action_location);
DrawableUtils.setTintDrawable(location.getIcon(), Color.WHITE);

Введите описание изображения здесь