Saat saya menggunakan drawable dari AppCompat
perpustakaan untuk Toolbar
item menu saya , pewarnaan berfungsi seperti yang diharapkan. Seperti ini:
<item
android:id="@+id/action_clear"
android:icon="@drawable/abc_ic_clear_mtrl_alpha" <-- from AppCompat
android:title="@string/clear" />
Tetapi jika saya menggunakan drawable saya sendiri atau bahkan menyalin drawable dari AppCompat
perpustakaan ke proyek saya sendiri, gambar tersebut tidak akan menghasilkan warna sama sekali.
<item
android:id="@+id/action_clear"
android:icon="@drawable/abc_ic_clear_mtrl_alpha_copy" <-- copy from AppCompat
android:title="@string/clear" />
Apakah ada keajaiban khusus dalam satu AppCompat
Toolbar
-satunya warna yang dapat digambar dari perpustakaan itu? Adakah cara untuk membuatnya bekerja dengan drawable saya sendiri?
Menjalankan ini di perangkat API Level 19 dengan compileSdkVersion = 21
dan targetSdkVersion = 21
, dan juga menggunakan semuanya dariAppCompat
abc_ic_clear_mtrl_alpha_copy
adalah salinan persis dari abc_ic_clear_mtrl_alpha
png dariAppCompat
Edit:
Pewarnaannya didasarkan pada nilai yang telah saya tetapkan android:textColorPrimary
di tema saya.
Misalnya <item name="android:textColorPrimary">#00FF00</item>
akan memberi saya warna tint hijau.
Screenshot
Tinting berfungsi seperti yang diharapkan dengan drawable dari AppCompat
Pewarnaan tidak berfungsi dengan drawable yang disalin dari AppCompat
Jawaban:
Karena jika Anda melihat kode sumber TintManager di AppCompat, Anda akan melihat:
/** * 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 };
Yang berarti mereka memiliki resourceIds tertentu yang masuk daftar putih untuk diwarnai.
Tapi saya rasa Anda selalu bisa melihat bagaimana mereka mewarnai gambar-gambar itu dan melakukan hal yang sama. Ini semudah mengatur ColorFilter pada drawable.
sumber
Setelah pustaka Dukungan v22.1 baru, Anda dapat menggunakan sesuatu yang mirip dengan ini:
@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; }
sumber
setColorFilter()
lebih disukai.Menetapkan
ColorFilter
(tint) pada aMenuItem
itu sederhana. Berikut ini contohnya: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); }
Kode di atas sangat membantu jika Anda ingin mendukung tema yang berbeda dan Anda tidak ingin memiliki salinan tambahan hanya untuk warna atau transparansi.
Klik di sini untuk kelas helper untuk menyetel
ColorFilter
semua drawable dalam menu, termasuk ikon luapan.Di
onCreateOptionsMenu(Menu menu)
panggil sajaMenuColorizer.colorMenu(this, menu, color);
setelah menggembungkan menu dan voila Anda; ikon Anda diwarnai.sumber
app:iconTint
atribut diimplementasikanSupportMenuInflater
dari pustaka dukungan (setidaknya di 28.0.0).Berhasil diuji dengan API 15 dan lebih tinggi.
File sumber daya menu:
<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>
(Dalam hal ini
?attr/appIconColorEnabled
adalah atribut warna khusus dalam tema aplikasi, dan sumber daya ikonnya adalah vektor yang dapat digambar.)sumber
android:iconTint
danandroid:iconTintMode
tidak berfungsi, tetapi mengawali denganapp:
alih - alihandroid:
berfungsi seperti pesona (pada drawable vektor saya sendiri, API> = 21)SupportMenuInflater
tidak akan menerapkan logika khusus apa pun jika menu bukanSupportMenu
sejenisnyaMenuBuilder
, itu hanya kembali ke regulerMenuInflater
.AppCompatActivity.startSupportActionMode(callback)
dan implementasi dukungan yang sesuai dariandroidx.appcompat
akan diteruskan ke callback.Saya pribadi lebih suka pendekatan ini dari tautan ini
Buat tata letak XML dengan berikut ini:
<?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"/>
dan rujuk drawable ini dari menu Anda:
<item android:id="@+id/option_menu_item_something" android:icon="@drawable/ic_action_something_tined"
sumber
bitmap
. Ada cara lain untuk mewarnai vektor. Mungkin seseorang bisa menambahkan pewarnaan vektor di sini juga ...Sebagian besar solusi di utas ini menggunakan API yang lebih baru, atau menggunakan refleksi, atau menggunakan pencarian tampilan intensif untuk menuju ke peningkatan
MenuItem
.Namun, ada pendekatan yang lebih elegan untuk melakukan itu. Anda memerlukan Toolbar kustom, karena kasus penggunaan "terapkan warna kustom" tidak cocok dengan API penataan gaya / tema publik.
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) ); } }
Pastikan Anda memanggil kode Aktivitas / Fragmen Anda:
toolbar.inflateMenu(R.menu.some_menu); toolbar.setOnMenuItemClickListener(someListener);
Tidak ada refleksi, tidak ada pencarian tampilan, dan tidak banyak kode, ya?
Dan sekarang Anda bisa mengabaikan yang konyol
onCreateOptionsMenu/onOptionsItemSelected
.sumber
Menu#getItem()
kompleksitas adalah O (1) di toolbar, karena item disimpan di ArrayList. Yang berbeda dariView#findViewById
traversal (yang saya sebut view lookup dalam jawaban saya), kompleksitasnya jauh dari konstan :-)Berikut adalah solusi yang saya gunakan; Anda bisa memanggilnya setelah onPrepareOptionsMenu () atau tempat yang setara. Alasan mutate () adalah jika Anda menggunakan ikon di lebih dari satu lokasi; tanpa mutasi, semuanya akan memiliki warna yang sama.
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); } } } } }
Ini tidak akan menangani overflow, tetapi untuk itu, Anda dapat melakukan ini:
Tata Letak:
<android.support.v7.widget.Toolbar ... android:theme="@style/myToolbarTheme" />
Gaya:
<style name="myToolbarTheme"> <item name="colorControlNormal">#FF0000</item> </style>
Ini berfungsi pada appcompat v23.1.0.
sumber
Ini berhasil untuk saya:
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 }
Atau Anda dapat menggunakan tint dalam 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>
sumber