AppCompat Araç Çubuğunda MenuItem renklendirme


95

Menü AppCompatöğelerim için kitaplıktan çizimleri kullandığımda Toolbarrenklendirme beklendiği gibi çalışıyor. Bunun gibi:

<item
    android:id="@+id/action_clear"
    android:icon="@drawable/abc_ic_clear_mtrl_alpha"  <-- from AppCompat
    android:title="@string/clear" />

Ancak kendi çekilebilir öğelerimi kullanırsam veya hatta çekilebilir öğeleri AppCompatkitaplıktan kendi projeme kopyalarsam hiç renklenmeyecektir.

<item
    android:id="@+id/action_clear"
    android:icon="@drawable/abc_ic_clear_mtrl_alpha_copy"  <-- copy from AppCompat
    android:title="@string/clear" />

AppCompat ToolbarSadece o kitaplıktan çekilebilen renk tonlarında özel bir sihir var mı ? Bunu kendi çekmecelerimle çalıştırmanın bir yolu var mı?

Bunu API Seviye 19 cihazında compileSdkVersion = 21ve targetSdkVersion = 21ile ve ayrıcaAppCompat

abc_ic_clear_mtrl_alpha_copyabc_ic_clear_mtrl_alphapng'nin tam bir kopyasıdır.AppCompat

Düzenle:

Renklendirme, temamda belirlediğim değere dayanmaktadır android:textColorPrimary.

Örneğin <item name="android:textColorPrimary">#00FF00</item>, bana yeşil bir renk tonu verirdi.

Ekran görüntüleri

AppCompat'tan çekilebilir özellik ile beklendiği gibi çalışan renklendirme AppCompat'tan çekilebilir özellik ile beklendiği gibi çalışan renklendirme

AppCompat'tan kopyalanan çekilebilir ile renklendirme çalışmıyor AppCompat'tan kopyalanan çekilebilir ile renklendirme çalışmıyor


Her iki stilin de ebeveyni aynı mı? Ya en üst stili kendi tarzınızla genişletirseniz?
G_V

Tarzlarda fark yok. Tek fark, her ikisi de .png dosyası olan çekilebilir dosya
greve

Çizilebilir, koddaki orijinal AppCombat çizilebilirinin tam bir kopyası gibi görünüyor?
G_V

Kopyaladığım png dosyaları. Onlar tamamen aynı.
greve

Peki, aynı stile ve aynı görüntüye sahipse, kodunuz orijinalinden tam olarak nerede farklıdır?
G_V

Yanıtlar:


31

Çünkü AppCompat'ta TintManager'ın kaynak koduna bakarsanız şunu göreceksiniz:

/**
 * 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
};

Bu da, renklendirilmek üzere beyaz listeye alınmış belirli kaynak kimliklerine sahip oldukları anlamına gelir.

Ama sanırım bu görüntüleri nasıl renklendirdiklerini her zaman görebilir ve aynısını yapabilirsiniz. ColorFilter'ı bir çekmeceye ayarlamak kadar kolaydır.


Ugh, korktuğum şey buydu. AppCompat için kaynak kodunu SDK'da bulamadım, bu yüzden bu parçayı kendim bulamadım. O zaman googlesource.com'a göz atmam gerekecek sanırım. Teşekkürler!
greve

8
Bunun teğetsel bir soru olduğunu biliyorum ama neden beyaz liste var? Bu simgelerle renklendirme yapabiliyorsa, neden kendi simgelerimizi renklendiremiyoruz? Ayrıca, en önemli şeylerden birini dışarıda bıraktığınızda neredeyse her şeyi geriye dönük uyumlu hale getirmenin (AppCompat ile) amacı nedir: işlem çubuğu simgeleri (özel renkle).
Zsolt Safrany 09

1
Google'ın sorun izleyicisinde düzeltilmiş olarak işaretlenmiş bir sorun var, ancak benim için çalışmıyor, ancak buradan izleyebilirsiniz: issuetracker.google.com/issues/37127128
niknetniko

Bunun düzeltildiğini iddia ediyorlar, ama değil. Tanrım, Android tema motorundan, AppCompat'tan ve onunla ilgili tüm saçmalıklardan nefret ediyorum. Yalnızca "Github repo browser" örnek uygulamaları için çalışır.
Martin Marconcini

98

Yeni Destek kitaplığı v22.1'den sonra, buna benzer bir şey kullanabilirsiniz:

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

1
Bu durumda eskinin daha setColorFilter()çok tercih edildiğini söyleyebilirim .
natario

@mvai, setColorFilter () neden daha çok tercih edilir?
wilddev

4
@wilddev kısalık. Menu.findItem (). GetIcon (). SetColorFilter () 'e gidebildiğinizde neden DrawableCompat sınıfını destekleyesiniz? Tek astar ve temiz.
natario

5
Tek satırlık argüman, mantığın tamamını kendi TintingUtils.tintMenuIcon (...) yönteminize veya onu adlandırmak istediğiniz her şeye soyutladığınızda alakasızdır. İleride mantığı değiştirmeniz veya ayarlamanız gerekirse, bunu uygulamanın her yerinde değil, tek bir yerde yaparsınız.
Dan Dar3

Renklendirmek için farklı mantıklara ve gelecekte değişmemeye gerek yoktur.
Jemshit Iskenderov

84

A üzerinde a ColorFilter(ton) ayarlamak MenuItembasittir. İşte bir örnek:

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

Farklı temaları desteklemek istiyorsanız ve yalnızca renk veya şeffaflık için fazladan kopyalar istemiyorsanız, yukarıdaki kod çok yararlıdır.

ColorFilterTaşma simgesi de dahil olmak üzere bir menüdeki tüm çekilebilir öğeleriçin bir yardımcı sınıf için burayı tıklayın .

Sadece menünüzü ve işinizi şişirdikten sonra onCreateOptionsMenu(Menu menu)arayın MenuColorizer.colorMenu(this, menu, color);; simgeleriniz renklidir.


4
Başımı masama vurup tüm simgelerimin neden renklendiğini anlamaya çalışıyorum, drawable.mutate () hakkındaki uyarılar için teşekkürler!
Scott Cooper

54

app:iconTintözellik, SupportMenuInflaterdestek kitaplığından (en az 28.0.0'da) uygulanır.

API 15 ve üzeri ile başarıyla test edildi.

Menü kaynak dosyası:

<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>

(Bu durumda ?attr/appIconColorEnabled, uygulamanın temalarında özel bir renk özelliğiydi ve simge kaynakları, vektör çizimleri idi.)


5
Bu kabul edilen yeni cevap olmalı! Ayrıca, lütfen not edin android:iconTintve android:iconTintModeçalışmayın, ancak bir cazibe gibi işler app:yerine ön ek android:(kendi vektör çekilebilirlerimde, API> = 21)
Sebastiaan Alvarez Rodriguez

Programlı arayarak edin: notu SupportMenuInflatermenüsü değilse herhangi bir özel mantık geçerli olmayacaktır SupportMenugibi MenuBuilder, sadece düzenli geri düşer MenuInflater.
geekley

Bu durumda, kullanım AppCompatActivity.startSupportActionMode(callback)ve uygun destek uygulamaları androidx.appcompatgeri aramaya aktarılacaktır.
geekley

30

Ben şahsen bu yaklaşımı bu bağlantıdan tercih ettim

Aşağıdakilerle bir XML düzeni oluşturun:

<?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"/>

ve menünüzden bu çekilişi referans alın:

<item
    android:id="@+id/option_menu_item_something"
    android:icon="@drawable/ic_action_something_tined"

2
Bu bağlantı soruyu cevaplayabilirken, cevabın temel kısımlarını buraya eklemek ve referans için bağlantıyı sağlamak daha iyidir. Bağlantılı sayfa değişirse yalnızca bağlantı yanıtları geçersiz hale gelebilir.
tomloprod

Yorumunuz için teşekkür ederim, soruyu düzenledim. @tomloprod
N Jay

4
Bu benim tercih ettiğim çözüm. Ancak, kaynak olarak yeni vektör çekilebilir türleri kullandığınızda şimdilik bu çözümün işe yaramadığını unutmamak önemlidir.
Michael De Soto

1
@haagmm bu çözüm API> = 21'e ihtiyaç duyar. Ayrıca vektörler için de çalışır.
Neurotransmitter

1
Ve vektörlerle çalışmamalı, kök etiket bitmap. Vektörleri renklendirmenin başka yolları da var. Belki birileri buraya vektör boyama ekleyebilir ...
milosmns

11

Bu iş parçacığındaki çözümlerin çoğu ya daha yeni bir API kullanıyor ya da yansıma kullanıyor ya da şişirilmiş olana ulaşmak için yoğun görünüm araması kullanıyor MenuItem.

Ancak, bunu yapmak için daha zarif bir yaklaşım var. "Özel renk tonu uygula" kullanım durumunuz genel stil / tema API'si ile iyi oynamadığı için özel bir Araç Çubuğu'na ihtiyacınız vardır.

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

}

Etkinlik / Parça kodunuzu çağırdığınızdan emin olun:

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

Yansıma yok, görünüm araması yok ve çok fazla kod yok, ha?

Ve şimdi saçma olanı görmezden gelebilirsin onCreateOptionsMenu/onOptionsItemSelected.


Teknik olarak, bir görünüm araması yapıyorsunuz. Görünümleri yineliyor ve boş olmadıklarından emin oluyorsunuz. ;)
Martin Marconcini

Kesinlikle bir şekilde haklısınız :-) Yine de, Menu#getItem()araç çubuğundaki karmaşıklık O (1) 'dir, çünkü öğeler ArrayList'te saklanır. Karmaşıklığı sabit olmaktan uzak olan View#findViewById(cevabımda görünüm araması olarak bahsettiğim) geçişten farklı olan :-)
Drew

Kabul ettim, aslında çok benzer bir şey yaptım. Android'in bunca yıldan sonra tüm bunları düzenlememesine hâlâ şok oldum…
Martin Marconcini

Bu yaklaşımla taşma ikonu ve hamburger ikonu renklerini nasıl değiştirebilirim?
Sandra

8

İşte kullandığım çözüm; onPrepareOptionsMenu () veya eşdeğer bir yerden sonra çağırabilirsiniz. Mutate () işlevinin nedeni, simgeleri birden fazla konumda kullanmanızdır; mutasyon olmadan hepsi aynı renk tonunu alacak.

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

Bu, taşmayı ortadan kaldırmaz, ancak bunun için şunu yapabilirsiniz:

Yerleşim:

<android.support.v7.widget.Toolbar
    ...
    android:theme="@style/myToolbarTheme" />

Stiller:

<style name="myToolbarTheme">
        <item name="colorControlNormal">#FF0000</item>
</style>

Bu, appcompat v23.1.0'dan itibaren çalışır.


2

Bu benim için çalıştı:

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
    }

Veya çekilebilir xml'de renk tonu kullanabilirsiniz:

<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>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.