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" />
Есть ли какая-то особая магия в AppCompat
Toolbar
, которая отображает только выделенные строки из этой библиотеки? Любой способ заставить это работать с моими собственными чертежами?
Запуск этого на устройстве уровня API 19 с compileSdkVersion = 21
и targetSdkVersion = 21
, а также использование всего от AppCompat
abc_ic_clear_mtrl_alpha_copy
является точной копией abc_ic_clear_mtrl_alpha
png из AppCompat
Edit:
Тонирование основывается на значении, которое я установил для android:textColorPrimary
в моей теме.
например. <item name="android:textColorPrimary">#00FF00</item>
даст мне зеленый цвет оттенков.
Скриншоты
Оттенок работает, как ожидается, с помощью AppCompat
![Tinting working as expected with drawable from AppCompat]()
Оттенок не работает с возможностью копирования из AppCompat
![Tinting not working with drawable copied from AppCompat]()
Ответы
Ответ 1
Потому что, если вы посмотрите на исходный код 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.
Ответ 2
После новой библиотеки поддержки 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;
}
Ответ 3
Установка a ColorFilter
(оттенок) на MenuItem
проста. Вот пример:
Drawable drawable = menuItem.getIcon();
if (drawable != null) {
// If we don't mutate the drawable, then all drawable 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);
после раздувания меню и вуаля; ваши значки затемнены.
Ответ 4
Я лично предпочел этот подход из этого link
Создайте 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"
Ответ 5
Большинство решений в этом потоке либо используют более новый 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
.
Ответ 6
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
был пользовательским атрибутом цвета в темах приложения, а ресурсы значков были векторными объектами рисования.)
Ответ 7
Вот решение, которое я использую; вы можете вызвать его после 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.
Ответ 8
Создание нарисованного XML, вызывающего его как значок, имеет смысл. С помощью drawable можно манипулировать очень многими вещами... Например, в приведенном ниже значке меню я хочу сделать его белым... Я буду выполнять следующие шаги...
-
Создайте отрисовываемый XML- файл, как показано ниже: (пусть он называется " iconTinter.xml ")
-
Вызовите drawable в вашем XML файле меню, как показано ниже:
-
Запустите ваше приложение.