Jak zmienić kolor tła menu opcji?

98

Próbuję zmienić domyślny kolor menu opcji, który jest biały: chcę mieć czarne tło dla każdej pozycji w menu opcji.

Wypróbowałem kilka sesji, takich jak android: itemBackground = "# 000000" na elemencie item w elemencie menu, ale to nie zadziałało.

Jak mogę to osiągnąć?

feragusper
źródło

Odpowiedzi:

66

Po spędzeniu znacznej ilości czasu na wypróbowaniu wszystkich opcji, jedynym sposobem, w jaki mogłem uzyskać aplikację za pomocą AppCompat v7 do zmiany tła przepełnionego menu, było użycie atrybutu itemBackground:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    ...
    <item name="android:itemBackground">@color/overflow_background</item>
    ...
</style>

Testowane od API 4.2 do 5.0.

TheIT
źródło
2
To powinna być przyjęta odpowiedź, łatwa i prosta.
Alex Ardavin
4
Ale to usuwa efekt tętnienia: / W każdym razie, aby to odłożyć?
David Velasquez
Chcę zmienić tło całego okna, a nie osobne tło elementu, na przykład doda obramowanie do każdego elementu, jeśli ustawisz takie tło (z obramowaniami) ...
użytkownik25
1
A co z kolorem tekstu w menu?
doctorram
51

Jest to wyraźnie problem, który ma wielu programistów i któremu Google jeszcze nie zapewniło satysfakcjonującego, obsługiwanego rozwiązania.

Wokół postów na ten temat krąży wiele krzyżujących się intencji i nieporozumień, dlatego przed udzieleniem odpowiedzi prosimy o przeczytanie całej odpowiedzi.

Poniżej zamieszczam bardziej „dopracowaną” i dobrze skomentowaną wersję hackowania z innych odpowiedzi na tej stronie, zawierającą również pomysły z tych bardzo ściśle powiązanych pytań:

Zmień kolor tła menu Androida

Jak zmienić kolor tła menu opcji?

Android: dostosuj menu aplikacji (np. Kolor tła)

http://www.macadamian.com/blog/post/android_-_theming_the_unthemable/

Przycisk przełącznika elementu menu Android

Czy można ustawić nieprzezroczyste tło menu opcji Androida?

http://www.codeproject.com/KB/android/AndroidMenusMyWay.aspx

Ustawianie nieprzezroczystego tła menu

Przetestowałem ten hack na 2.1 (symulator), 2.2 (2 prawdziwe urządzenia) i 2.3 (2 prawdziwe urządzenia). Nie mam jeszcze żadnych tabletów 3.X do przetestowania, ale opublikuję tutaj wszelkie potrzebne zmiany, kiedy / jeśli to zrobię. Biorąc pod uwagę, że tablety 3.X używają pasków akcji zamiast menu opcji, jak wyjaśniono tutaj:

http://developer.android.com/guide/topics/ui/menus.html#options-menu

ten hack prawie na pewno nic nie da (nic złego i nic dobrego) na tabletach 3.X.

OPIS PROBLEMU (przeczytaj to przed odpowiedzią wyzwalającą z negatywnym komentarzem):

Menu Opcje ma bardzo różne style na różnych urządzeniach. Czysta czerń z białym tekstem na niektórych, czysta biel z czarnym tekstem na niektórych. Ja i wielu innych programistów chcemy kontrolować kolor tła komórek menu Opcje, a także kolor tekstu menu Opcje .

Niektórzy programiści aplikacji muszą tylko ustawić kolor tła komórki (nie kolor tekstu) i mogą to zrobić w bardziej przejrzysty sposób, korzystając ze stylu android: panelFullBackground opisanego w innej odpowiedzi. Jednak obecnie nie ma możliwości kontrolowania koloru tekstu w menu Opcje za pomocą stylów, więc można użyć tej metody tylko do zmiany koloru tła na inny, który nie spowoduje „zniknięcia” tekstu.

Chcielibyśmy to zrobić za pomocą udokumentowanego, przyszłościowego rozwiązania, ale jedno z nich jest po prostu niedostępne w systemie Android <= 2.3. Musimy więc użyć rozwiązania, które działa w aktualnych wersjach i jest zaprojektowane tak, aby zminimalizować ryzyko awarii / zepsucia w przyszłych wersjach. Chcemy rozwiązania, które wdzięcznie zawiedzie i przywróci domyślne zachowanie, jeśli musi zawieść.

Istnieje wiele uzasadnionych powodów, dla których może być konieczne kontrolowanie wyglądu menu opcji (zazwyczaj w celu dopasowania stylu wizualnego do reszty aplikacji), więc nie będę się nad tym rozwodzić.

Jest opublikowany błąd Google Android na ten temat: dodaj swoje wsparcie, oznaczając ten błąd gwiazdką (pamiętaj, że Google odradza komentarze „ja też”: wystarczy gwiazdka):

http://code.google.com/p/android/issues/detail?id=4441

PODSUMOWANIE DOTYCHCZASOWYCH ROZWIĄZAŃ:

Kilka plakatów sugerowało włamanie do LayoutInflater.Factory. Sugerowany hack działał dla Androida <= 2.2 i nie powiódł się dla Androida 2.3, ponieważ hack przyjął nieudokumentowane założenie: można wywołać LayoutInflater.getView () bezpośrednio, bez bieżącego wywoływania LayoutInflater.inflate () w tej samej instancji LayoutInflater. Nowy kod w systemie Android 2.3 złamał to założenie i doprowadził do wyjątku NullPointerException.

Mój nieco dopracowany hack poniżej nie opiera się na tym założeniu.

Co więcej, ataki polegają również na użyciu wewnętrznej, nieudokumentowanej nazwy klasy „com.android.internal.view.menu.IconMenuItemView” jako ciągu znaków (a nie typu Java). Nie widzę sposobu, aby tego uniknąć i nadal osiągać wyznaczony cel. Możliwe jest jednak ostrożne włamanie, które może się cofnąć, jeśli „com.android.internal.view.menu.IconMenuItemView” nie pojawi się w bieżącym systemie.

Ponownie, zrozum, że to hack i w żadnym wypadku nie twierdzę, że będzie działać na wszystkich platformach. Ale my, programiści, nie żyjemy w fantastycznym świecie akademickim, w którym wszystko musi być zgodne z książką: mamy problem do rozwiązania i musimy go rozwiązać najlepiej, jak potrafimy. Na przykład wydaje się mało prawdopodobne, że „com.android.internal.view.menu.IconMenuItemView” będzie istnieć na tabletach 3.X, ponieważ używają one pasków akcji zamiast menu opcji.

Wreszcie, niektórzy programiści rozwiązali ten problem, całkowicie pomijając menu opcji Androida i pisząc własną klasę menu (zobacz niektóre z linków powyżej). Nie próbowałem tego, ale jeśli masz czas na napisanie własnego widoku i wymyślenie, jak zastąpić widok Androida (jestem pewien, że diabeł tkwi tutaj w szczegółach), może to być fajne rozwiązanie, które nie wymaga żadnego nieudokumentowane hacki.

WŁAMAĆ SIĘ:

Oto kod.

Aby użyć tego kodu, wywołaj addOptionsMenuHackerInflaterFactory () RAZ w swojej aktywności onCreate () lub swojej aktywności onCreateOptionsMenu (). Ustawia domyślną fabrykę, która wpłynie na późniejsze tworzenie dowolnego menu opcji. Nie ma to wpływu na menu opcji, które zostały już utworzone (poprzednie hacki używały nazwy funkcji setMenuBackground (), co jest bardzo mylące, ponieważ funkcja nie ustawia żadnych właściwości menu przed powrotem).

@SuppressWarnings("rawtypes")
static Class       IconMenuItemView_class = null;
@SuppressWarnings("rawtypes")
static Constructor IconMenuItemView_constructor = null;

// standard signature of constructor expected by inflater of all View classes
@SuppressWarnings("rawtypes")
private static final Class[] standard_inflater_constructor_signature = 
new Class[] { Context.class, AttributeSet.class };

protected void addOptionsMenuHackerInflaterFactory()
{
    final LayoutInflater infl = getLayoutInflater();

    infl.setFactory(new Factory()
    {
        public View onCreateView(final String name, 
                                 final Context context,
                                 final AttributeSet attrs)
        {
            if (!name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView"))
                return null; // use normal inflater

            View view = null;

            // "com.android.internal.view.menu.IconMenuItemView" 
            // - is the name of an internal Java class 
            //   - that exists in Android <= 3.2 and possibly beyond
            //   - that may or may not exist in other Android revs
            // - is the class whose instance we want to modify to set background etc.
            // - is the class we want to instantiate with the standard constructor:
            //     IconMenuItemView(context, attrs)
            // - this is what the LayoutInflater does if we return null
            // - unfortunately we cannot just call:
            //     infl.createView(name, null, attrs);
            //   here because on Android 3.2 (and possibly later):
            //   1. createView() can only be called inside inflate(),
            //      because inflate() sets the context parameter ultimately
            //      passed to the IconMenuItemView constructor's first arg,
            //      storing it in a LayoutInflater instance variable.
            //   2. we are inside inflate(),
            //   3. BUT from a different instance of LayoutInflater (not infl)
            //   4. there is no way to get access to the actual instance being used
            // - so we must do what createView() would have done for us
            //
            if (IconMenuItemView_class == null)
            {
                try
                {
                    IconMenuItemView_class = getClassLoader().loadClass(name);
                }
                catch (ClassNotFoundException e)
                {
                    // this OS does not have IconMenuItemView - fail gracefully
                    return null; // hack failed: use normal inflater
                }
            }
            if (IconMenuItemView_class == null)
                return null; // hack failed: use normal inflater

            if (IconMenuItemView_constructor == null)
            {
                try
                {
                    IconMenuItemView_constructor = 
                    IconMenuItemView_class.getConstructor(standard_inflater_constructor_signature);
                }
                catch (SecurityException e)
                {
                    return null; // hack failed: use normal inflater
                }
                catch (NoSuchMethodException e)
                {
                    return null; // hack failed: use normal inflater
                }
            }
            if (IconMenuItemView_constructor == null)
                return null; // hack failed: use normal inflater

            try
            {
                Object[] args = new Object[] { context, attrs };
                view = (View)(IconMenuItemView_constructor.newInstance(args));
            }
            catch (IllegalArgumentException e)
            {
                return null; // hack failed: use normal inflater
            }
            catch (InstantiationException e)
            {
                return null; // hack failed: use normal inflater
            }
            catch (IllegalAccessException e)
            {
                return null; // hack failed: use normal inflater
            }
            catch (InvocationTargetException e)
            {
                return null; // hack failed: use normal inflater
            }
            if (null == view) // in theory handled above, but be safe... 
                return null; // hack failed: use normal inflater


            // apply our own View settings after we get back to runloop
            // - android will overwrite almost any setting we make now
            final View v = view;
            new Handler().post(new Runnable()
            {
                public void run()
                {
                    v.setBackgroundColor(Color.BLACK);

                    try
                    {
                        // in Android <= 3.2, IconMenuItemView implemented with TextView
                        // guard against possible future change in implementation
                        TextView tv = (TextView)v;
                        tv.setTextColor(Color.WHITE);
                    }
                    catch (ClassCastException e)
                    {
                        // hack failed: do not set TextView attributes
                    }
                }
            });

            return view;
        }
    });
}

Dziękuję za przeczytanie i miłej zabawy!

Louis Semprini
źródło
15
Jedyne, co niezawodnie otrzymuję, próbując użyć tego (i podobnego rozwiązania), to `java.lang.IllegalStateException: Fabryka została już ustawiona na tym LayoutInflater`
Bostone
Pracuje dla mnie! Tak wspaniale, że wreszcie mamy rozwiązanie! Testowane na pierniku, plastrze miodu i ICS
Chad Schultz
Przetestowane w Samsung Galaxy Nexus (4.1.1) i działa! Dobra rzecz, Louis!
Felipe Caldas
2
Działa na Galaxy Nexus 7 (4.1.1), jednak kolor tekstu jest przywracany przy każdym kolejnym wywołaniu menu po pierwszym ukryciu.
Will Kru,
1
Dostaję też wyjątek IllegalStateException. Wygląda na to, że hack jest niezgodny z ActionBarSherlock, którego używam.
Travis
20

Atrybut stylu tła menu to android:panelFullBackground.

Pomimo tego, co mówi dokumentacja, musi to być zasób (np. @android:color/blackLub @drawable/my_drawable), ulegnie awarii, jeśli bezpośrednio użyjesz wartości koloru.

Pozwoli to również usunąć obramowania przedmiotów, których nie mogłem zmienić ani usunąć za pomocą rozwiązania Primalpop.

Jeśli chodzi o kolor tekstu, nie znalazłem sposobu, aby ustawić go za pomocą stylów w 2.2 i jestem pewien, że próbowałem wszystkiego (w ten sposób odkryłem atrybut tła menu). Musiałbyś użyć do tego rozwiązania Primalpop.

Pilot_51
źródło
3
Gdzie mam ustawić tę wartość? Nie mogłem go uruchomić na Androidzie 2.2. lub 2.3
Janusz
1
@Janusz In Styles.xml. To prawdopodobnie pomoże: developer.android.com/guide/topics/resources/ ...
Pilot_51
2
Nie działa, fajnie, gdybyś mógł określić, dokąd ma iść, próbowałem wszędzie, z wyjątkiem tworzenia innego stylu dla moich elementów menu, aby przypisać .....
John
14

W przypadku Androida 2.3 można to zrobić za pomocą bardzo ciężkiego hakowania:

Główną przyczyną problemów z Androidem 2.3 jest to, że w LayoutInflater mConstructorArgs [0] = mContext jest ustawiane tylko podczas wykonywania wywołań

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/android/view/LayoutInflater.java/#352

protected void setMenuBackground(){

    getLayoutInflater().setFactory( new Factory() {

        @Override
        public View onCreateView (final String name, final Context context, final AttributeSet attrs ) {

            if ( name.equalsIgnoreCase( "com.android.internal.view.menu.IconMenuItemView" ) ) {

                try { // Ask our inflater to create the view
                    final LayoutInflater f = getLayoutInflater();
                    final View[] view = new View[1]:
                    try {
                        view[0] = f.createView( name, null, attrs );
                    } catch (InflateException e) {
                        hackAndroid23(name, attrs, f, view);
                    }
                    // Kind of apply our own background
                    new Handler().post( new Runnable() {
                        public void run () {
                            view.setBackgroundResource( R.drawable.gray_gradient_background);
                        }
                    } );
                    return view;
                }
                catch ( InflateException e ) {
                }
                catch ( ClassNotFoundException e ) {
                }
            }
            return null;
        }
    });
}

static void hackAndroid23(final String name,
    final android.util.AttributeSet attrs, final LayoutInflater f,
    final TextView[] view) {
    // mConstructorArgs[0] is only non-null during a running call to inflate()
    // so we make a call to inflate() and inside that call our dully XmlPullParser get's called
    // and inside that it will work to call "f.createView( name, null, attrs );"!
    try {
        f.inflate(new XmlPullParser() {
            @Override
            public int next() throws XmlPullParserException, IOException {
                try {
                    view[0] = (TextView) f.createView( name, null, attrs );
                } catch (InflateException e) {
                } catch (ClassNotFoundException e) {
                }
                throw new XmlPullParserException("exit");
            }   
        }, null, false);
    } catch (InflateException e1) {
        // "exit" ignored
    }
}

Przetestowałem go, aby działał na Androidzie 2.3 i nadal działał na wcześniejszych wersjach. Jeśli coś znowu się zepsuje w późniejszych wersjach Androida, zamiast tego zobaczysz po prostu domyślny styl menu

Marcus Wolschon
źródło
Ten kod działa tylko do wersji 2.1 Ten kod wydaje się być lepszy: stackoverflow.com/questions/2944244/ ...
Felipe Caldas
Cześć, użyłem twojej funkcji, ale otrzymałem następujący błąd Błąd napełniania klasy com.android.internal.view.menu.IconMenuItemView i jeszcze jeden wyjątek Błąd nadmuchiwania klasy <nieznany> ... teraz co mam teraz zrobić ... ? proszę pomóż mi.
Rushabh Patel
13

Właśnie napotkałem ten problem w aplikacji, która musiała być kompatybilna z Gingerbread i nadal zachowywać jak najwięcej stylizacji z urządzeń obsługujących Holo.

Znalazłem stosunkowo czyste rozwiązanie, które działało dobrze.

W motywie używam tła z 9 łatkami do rysowania, aby uzyskać niestandardowy kolor tła:

<style name="Theme.Styled" parent="Theme.Sherlock">
   ...
   <item name="android:panelFullBackground">@drawable/menu_hardkey_panel</item>
</style>

Zrezygnowałem ze stylizacji koloru tekstu i po prostu użyłem Spannable, aby ustawić kolor tekstu dla mojego elementu w kodzie:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
   MenuInflater inflater = getSupportMenuInflater();
   inflater.inflate(R.menu.actions_main, menu);

   if (android.os.Build.VERSION.SDK_INT < 
        android.os.Build.VERSION_CODES.HONEYCOMB) {

        SpannableStringBuilder text = new SpannableStringBuilder();
        text.append(getString(R.string.action_text));
        text.setSpan(new ForegroundColorSpan(Color.WHITE), 
                0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        MenuItem item1 = menu.findItem(R.id.action_item1);
        item1.setTitle(text);
   }

   return true;
}
Nicolai Buch-Andersen
źródło
działa świetnie w przypadku mojego problemu z użyciem motywu ActionBarSherlock Light na urządzeniu Gingerbread! Dzięki temu mogę łatwo zmienić tło menu opcji na jasnoszare, a kolor tekstu na czarny (ikony są już czarne, jak w ActionBar! Dziękuję!
florianbaethge
13

Oto jak rozwiązałem swój. Właśnie określiłem kolor tła i kolor tekstu w stylach. czyli plik res> values> styles.xml.

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:itemBackground">#ffffff</item>
    <item name="android:textColor">#000000</item>
</style>
Bukunmi
źródło
1
textcolor zmieniłby się wszędzie
user3156040
zmienia tylko kolor tła elementów, a układ opcji menu ma dopełnienie od góry i od dołu, to nie pomaga
FarshidABZ
10

Należy zauważyć, że nadmiernie komplikujecie problem, tak jak wiele innych postów! Wszystko, co musisz zrobić, to utworzyć selektory do rysowania z dowolnym tłem i ustawić je na rzeczywiste elementy. Po prostu spędziłem dwie godziny próbując twoich rozwiązań (wszystkie sugerowane na tej stronie) i żadne z nich nie zadziałało. Nie wspominając o tym, że jest mnóstwo błędów, które zasadniczo spowalniają twoją wydajność w tych blokach try / catch, które masz.

W każdym razie tutaj jest plik xml menu:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/m1"
          android:icon="@drawable/item1_selector"
          />
    <item android:id="@+id/m2"
          android:icon="@drawable/item2_selector"
          />
</menu>

Teraz w twoim item1_selector:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/item_highlighted" />
    <item android:state_selected="true" android:drawable="@drawable/item_highlighted" />
    <item android:state_focused="true" android:drawable="@drawable/item_nonhighlighted" />
    <item android:drawable="@drawable/item_nonhighlighted" />
</selector>

Następnym razem, gdy zdecydujesz się pójść do supermarketu przez Kanadę, wypróbuj mapy Google!

dropsOfJupiter
źródło
W pełni się zgadzam. Po co wymyślać Androida na nowo, skoro on =) już istnieje?
Fredrik
Działa świetnie. Utwórz listę warstw, którą można rysować za pomocą ikony i żądanego tła. Jedynym problemem jest to, że nie wiem, czy mogę zmienić kolor tekstu. Dlatego nie każdy kolor tła działa
Janusz
53
Piękny, elegancki i całkowicie nie rozwiązuje problemu.
Aea
1
Jeśli się nie mylę, zmienia to tylko tło ikony, a nie samą pozycję menu, która pozostaje biała.
Jrom
3
To nie jest odpowiedź na pytanie. To jest zupełnie inna myśl.
Kostadin,
4
 <style name="AppThemeLight" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:itemBackground">#000000</item>
</style>

to działa dobrze dla mnie

Bincy Baby
źródło
jak zmienić kolor tytułu
parvez rafi
3
    /* 
     *The Options Menu (the one that pops up on pressing the menu button on the emulator) 
     * can be customized to change the background of the menu 
     *@primalpop  
   */ 

    package com.pop.menu;

    import android.app.Activity;
    import android.content.Context;
    import android.os.Bundle;
    import android.os.Handler;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.InflateException;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.View;
    import android.view.LayoutInflater.Factory;

    public class Options_Menu extends Activity {

        private static final String TAG = "DEBUG";

        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

        }

        /* Invoked when the menu button is pressed */

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // TODO Auto-generated method stub
            super.onCreateOptionsMenu(menu);
            MenuInflater inflater = new MenuInflater(getApplicationContext());
            inflater.inflate(R.menu.options_menu, menu);
            setMenuBackground();
            return true;
        }

        /*IconMenuItemView is the class that creates and controls the options menu 
         * which is derived from basic View class. So We can use a LayoutInflater 
         * object to create a view and apply the background.
         */
        protected void setMenuBackground(){

            Log.d(TAG, "Enterting setMenuBackGround");
            getLayoutInflater().setFactory( new Factory() {

                @Override
                public View onCreateView ( String name, Context context, AttributeSet attrs ) {

                    if ( name.equalsIgnoreCase( "com.android.internal.view.menu.IconMenuItemView" ) ) {

                        try { // Ask our inflater to create the view
                            LayoutInflater f = getLayoutInflater();
                            final View view = f.createView( name, null, attrs );
                            /* 
                             * The background gets refreshed each time a new item is added the options menu. 
                             * So each time Android applies the default background we need to set our own 
                             * background. This is done using a thread giving the background change as runnable
                             * object
                             */
                            new Handler().post( new Runnable() {
                                public void run () {
                                    view.setBackgroundResource( R.drawable.background);
                                }
                            } );
                            return view;
                        }
                        catch ( InflateException e ) {}
                        catch ( ClassNotFoundException e ) {}
                    }
                    return null;
                }
            });
        }
    }
Pierwotny Pappachan
źródło
3
Nie rób tego: name.equalsIgnoreCase ("com.android.internal.view.menu.IconMenuItemView" Jak nazwa wyraźnie wskazuje, używa prywatnych szczegółów implementacji, a tym samym może się zepsuć na dowolnej aktualizacji platformy lub urządzeniu.
hackbod
1
IconMenuItemView to klasa, która tworzy i kontroluje menu opcji, które pochodzi z podstawowej klasy View. Ta klasa pochodzi z kodu źródłowego Androida i jest obecna co najmniej od wersji interfejsu API 5. Nie widzę, aby zepsuła się na żadnej aktualizacji platformy ani urządzeniu.
Primal Pappachan
1
Nie możesz tego zobaczyć, ponieważ nie możesz zobaczyć przyszłości. Nawet jeśli istniał sposób, aby być pewnym, to jest to zła praktyka
HXCaine
Dzięki, przydaje się w razie potrzeby. Jednak nie działa w specjalnych przypadkach, takich jak elementy utworzone w onCreateOptionsMenu, ale wyłączone w onPrepareOptionsMenu, ale później włączone ponownie.
HRJ,
3

Dzięki Marcus! Działa płynnie w wersji 2.3, naprawiając niektóre błędy składniowe, oto poprawiony kod

    protected void setMenuBackground() {
    getLayoutInflater().setFactory(new Factory() {

        @Override
        public View onCreateView(final String name, final Context context,
                final AttributeSet attrs) {

            if (name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) {

                try { // Ask our inflater to create the view
                    final LayoutInflater f = getLayoutInflater();
                    final View[] view = new View[1];
                    try {
                        view[0] = f.createView(name, null, attrs);
                    } catch (InflateException e) {
                        hackAndroid23(name, attrs, f, view);
                    }
                    // Kind of apply our own background
                    new Handler().post(new Runnable() {
                        public void run() {
                            view[0].setBackgroundColor(Color.WHITE);

                        }
                    });
                    return view[0];
                } catch (InflateException e) {
                } catch (ClassNotFoundException e) {

                }
            }
            return null;
        }
    });
}

static void hackAndroid23(final String name,
        final android.util.AttributeSet attrs, final LayoutInflater f,
        final View[] view) {
    // mConstructorArgs[0] is only non-null during a running call to
    // inflate()
    // so we make a call to inflate() and inside that call our dully
    // XmlPullParser get's called
    // and inside that it will work to call
    // "f.createView( name, null, attrs );"!
    try {
        f.inflate(new XmlPullParser() {
            @Override
            public int next() throws XmlPullParserException, IOException {
                try {
                    view[0] = (TextView) f.createView(name, null, attrs);
                } catch (InflateException e) {
                } catch (ClassNotFoundException e) {
                }
                throw new XmlPullParserException("exit");
            }
        }, null, false);
    } catch (InflateException e1) {
        // "exit" ignored
    }
}
Halo Ha
źródło
1
Wszystko, co za to dostaję: java.lang.IllegalStateException: Fabryka została już ustawiona na tym LayoutInflater
Bostone
aby działał z ActionBarSherlock i frameworkiem kompatybilności i unikał IllegalStateException, zobacz tę sztuczkę stackoverflow.com/questions/13415284/ ...
avianey
3
protected void setMenuBackground() {
    getLayoutInflater().setFactory(new Factory() {
        @Override
        public View onCreateView (String name, Context context, AttributeSet attrs) {
            if (name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) {
                try {
                    // Ask our inflater to create the view
                    LayoutInflater f = getLayoutInflater();
                    final View view = f.createView(name, null, attrs);
                    // Kind of apply our own background
                    new Handler().post( new Runnable() {
                        public void run () {
                            view.setBackgroundResource(R.drawable.gray_gradient_background);
                        }
                    });
                    return view;
                }
                catch (InflateException e) {
                }
                catch (ClassNotFoundException e) {
                }
            }
            return null;
        }
    });
}

to jest plik XML

gradient 
    android:startColor="#AFAFAF" 
    android:endColor="#000000"
    android:angle="270"
shape
Android
źródło
1

Jeśli chcesz ustawić dowolny kolor, wydaje się, że działa to raczej dobrze androidx. Testowane na KitKat i Pie. Umieść to w swoim AppCompatActivity:

@Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
    if (name.equals("androidx.appcompat.view.menu.ListMenuItemView") &&
            parent.getParent() instanceof FrameLayout) {
            ((View) parent.getParent()).setBackgroundColor(yourFancyColor);
    }
    return super.onCreateView(parent, name, context, attrs);
}

To ustawia kolor android.widget.PopupWindow$PopupBackgroundView, który, jak można się domyślić, rysuje kolor tła. Nie ma przerysowania i możesz również użyć półprzezroczystych kolorów.

wiewiórka
źródło