¿Cómo cambiar el color de fondo del menú de opciones?

96

Estoy intentando cambiar el color predeterminado para el menú de opciones, que es blanco: quiero un fondo negro para cada elemento del menú de opciones.

Probé algunas sesiones como android: itemBackground = "# 000000" en el elemento del elemento dentro del elemento del menú, pero no funcionó.

¿Cómo puedo lograr esto?

feragusper
fuente

Respuestas:

65

Después de pasar una cantidad considerable de tiempo probando todas las opciones, la única forma en que pude obtener una aplicación usando AppCompat v7 para cambiar el fondo del menú de desbordamiento fue usando el atributo itemBackground:

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

Probado de API 4.2 a 5.0.

La cosa
fuente
2
Esta debería ser la respuesta aceptada, fácil y simple.
Alex Ardavin
3
Pero esto elimina el efecto dominó: / ¿De todos modos, devolverlo?
David Velasquez
Quiero cambiar el fondo de la ventana completa, no el fondo del elemento por separado, por ejemplo, agregará bordes a cada elemento si configura dicho fondo (con bordes) ...
usuario25
1
¿Qué pasa con el color del texto del menú?
doctorram
51

Este es claramente un problema que tienen muchos programadores y al que Google aún no ha proporcionado una solución satisfactoria y compatible.

Hay muchas intenciones cruzadas y malentendidos flotando alrededor de las publicaciones sobre este tema, así que lea esta respuesta completa antes de responder.

A continuación incluyo una versión más "refinada" y bien comentada del truco de otras respuestas en esta página, que también incorpora ideas de estas preguntas muy relacionadas:

Cambiar el color de fondo del menú de Android

¿Cómo cambiar el color de fondo del menú de opciones?

Android: personaliza el menú de la aplicación (por ejemplo, color de fondo)

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

Menú de Android Botón de alternancia de elementos

¿Es posible hacer que el fondo del menú de opciones de Android no sea translúcido?

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

Configurar el fondo del menú para que sea opaco

Probé este truco en 2.1 (simulador), 2.2 (2 dispositivos reales) y 2.3 (2 dispositivos reales). No tengo ninguna tableta 3.X para probar todavía, pero publicaré los cambios necesarios aquí cuando o si los tengo. Dado que las tabletas 3.X usan barras de acción en lugar de menús de opciones, como se explica aquí:

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

Es casi seguro que este truco no hará nada (ni daño ni beneficio) en tabletas 3.X.

EXPOSICIÓN DEL PROBLEMA (lea esto antes de disparar-responder con un comentario negativo):

El menú Opciones tiene estilos muy diferentes en diferentes dispositivos. Negro puro con texto blanco en algunos, blanco puro con texto negro en algunos. Yo y muchos otros desarrolladores deseamos controlar el color de fondo de las celdas del menú Opciones , así como el color del texto del menú Opciones .

Algunos desarrolladores de aplicaciones solo necesitan establecer el color de fondo de la celda (no el color del texto), y pueden hacerlo de una manera más limpia usando el estilo android: panelFullBackground descrito en otra respuesta. Sin embargo, actualmente no hay forma de controlar el color del texto del menú Opciones con estilos, por lo que solo se puede usar este método para cambiar el fondo a otro color que no haga que el texto "desaparezca".

Nos encantaría hacer esto con una solución documentada y preparada para el futuro, pero simplemente no hay una disponible a partir de Android <= 2.3. Por lo tanto, tenemos que usar una solución que funcione en las versiones actuales y que esté diseñada para minimizar las posibilidades de fallar o romperse en versiones futuras. Queremos una solución que vuelva a fallar con gracia al comportamiento predeterminado si tiene que fallar.

Hay muchas razones legítimas por las que uno puede necesitar controlar el aspecto de los menús de Opciones (generalmente para que coincida con un estilo visual para el resto de la aplicación), así que no me detendré en eso.

Hay un error de Google Android publicado sobre esto: agregue su apoyo destacando este error (tenga en cuenta que Google desalienta los comentarios de "yo también": solo una estrella es suficiente):

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

RESUMEN DE SOLUCIONES HASTA AHORA:

Varios carteles han sugerido un truco relacionado con LayoutInflater.Factory. El truco sugerido funcionó para Android <= 2.2 y falló para Android 2.3 porque el truco hizo una suposición no documentada: que uno podría llamar a LayoutInflater.getView () directamente sin estar actualmente dentro de una llamada a LayoutInflater.inflate () en la misma instancia de LayoutInflater. El nuevo código en Android 2.3 rompió esta suposición y condujo a una NullPointerException.

Mi truco ligeramente refinado a continuación no se basa en esta suposición.

Además, los hacks también se basan en el uso de un nombre de clase interno no documentado "com.android.internal.view.menu.IconMenuItemView" como una cadena (no como un tipo Java). No veo ninguna forma de evitar esto y aún así lograr el objetivo establecido. Sin embargo, es posible hacer el truco de una manera cuidadosa que retroceda si "com.android.internal.view.menu.IconMenuItemView" no aparece en el sistema actual.

Nuevamente, entienda que esto es un truco y de ninguna manera estoy afirmando que esto funcionará en todas las plataformas. Pero los desarrolladores no vivimos en un mundo académico de fantasía donde todo tiene que ser según las reglas: tenemos un problema que resolver y tenemos que resolverlo lo mejor que podamos. Por ejemplo, parece poco probable que "com.android.internal.view.menu.IconMenuItemView" exista en tabletas 3.X ya que utilizan barras de acción en lugar de menús de opciones.

Finalmente, algunos desarrolladores han resuelto este problema suprimiendo totalmente el menú de opciones de Android y escribiendo su propia clase de menú (consulte algunos de los enlaces anteriores). No he probado esto, pero si tiene tiempo para escribir su propia Vista y descubrir cómo reemplazar la vista de Android (estoy seguro de que el diablo está en los detalles aquí), entonces podría ser una buena solución que no requiere ninguna hacks indocumentados.

CORTAR A TAJOS:

Aquí está el código.

Para usar este código, llame a addOptionsMenuHackerInflaterFactory () UNA VEZ desde su actividad enCreate () o su actividad enCreateOptionsMenu (). Establece una fábrica predeterminada que afectará la creación posterior de cualquier menú de opciones. No afecta los menús de opciones que ya se han creado (los hacks anteriores usaban un nombre de función de setMenuBackground (), que es muy engañoso ya que la función no establece ninguna propiedad de menú antes de regresar).

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

¡Gracias por leer y disfrutar!

Louis Semprini
fuente
15
Lo único que obtengo de manera confiable al intentar usar esta (y una solución similar) es `java.lang.IllegalStateException: ya se ha configurado una fábrica en este LayoutInflater`
Bostone
¡Funciona para mi! ¡Qué bueno tener finalmente una solución! Probado en Gingerbread, Honeycomb e ICS
Chad Schultz
Probado en Samsung Galaxy Nexus (4.1.1) y funcionando. ¡Qué bueno, Louis!
Felipe Caldas
2
Funciona en Galaxy Nexus 7 (4.1.1), sin embargo, el color del texto se revierte para cada llamada posterior al menú después de ocultarse por primera vez.
Will Kru
1
También obtengo la IllegalStateException. Parece que el truco es incompatible con ActionBarSherlock, que estoy usando.
Travis
20

El atributo de estilo para el fondo del menú es android:panelFullBackground.

A pesar de lo que dice la documentación, debe ser un recurso (por ejemplo, @android:color/blacko @drawable/my_drawable), se bloqueará si usa un valor de color directamente.

Esto también eliminará los bordes de los elementos que no pude cambiar o eliminar con la solución de primalpop.

En cuanto al color del texto, no he encontrado ninguna forma de configurarlo a través de estilos en 2.2 y estoy seguro de que lo he probado todo (que es como descubrí el atributo de fondo del menú). Necesitarías usar la solución de primalpop para eso.

Pilot_51
fuente
3
¿Dónde tengo que establecer este valor? No pude hacer que funcione en Android 2.2. o 2.3
Janusz
1
@Janusz en Styles.xml. Esto probablemente ayudará: developer.android.com/guide/topics/resources/…
Pilot_51
2
No funciona, bueno si pudiera indicar dónde se supone que debe ir, lo intenté en todas partes excepto para hacer otro estilo para que los elementos de mi menú atribuyan .....
John
14

Para Android 2.3, esto se puede hacer con una piratería muy fuerte:

La causa principal de los problemas con Android 2.3 es que en LayoutInflater mConstructorArgs [0] = mContext solo se establece durante las llamadas en ejecución 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
    }
}

Lo probé para que funcione en Android 2.3 y aún funcione en versiones anteriores. Si algo se rompe nuevamente en versiones posteriores de Android, simplemente verá el estilo de menú predeterminado en su lugar

Marcus Wolschon
fuente
Este código solo se ejecuta hasta la versión 2.1 Este código aquí parece ser mejor: stackoverflow.com/questions/2944244/…
Felipe Caldas
Hola, he usado su función pero recibí este siguiente error Error al inflar la clase com.android.internal.view.menu.IconMenuItemView y luego una excepción más Error al inflar la clase <desconocido> ... ahora, ¿qué debo hacer ahora ... ? por favor, ayúdame.
Rushabh Patel
13

También encontré este problema en una aplicación que tenía que ser compatible con Gingerbread y aún conservar la mayor parte posible del estilo de los dispositivos habilitados para Holo.

Encontré una solución relativamente limpia, que funcionó bien para mí.

En el tema, uso un fondo dibujable de 9 parches para obtener un color de fondo personalizado:

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

Dejé de intentar diseñar el color del texto y solo usé un Spannable para establecer el color del texto de mi artículo en el código:

@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
fuente
funciona muy bien para mi problema con el uso del tema ActionBarSherlock Light en un dispositivo Gingerbread. Con esto, puede cambiar fácilmente el fondo del menú Opciones de un color gris y un texto claro al negro (iconos son de color negro como en la Barra de acciones Gracias!!
florianbaethge
12

Así es como resolví el mío. Acabo de especificar el color de fondo y el color del texto en los estilos. es decir, res> valores> archivo styles.xml.

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:itemBackground">#ffffff</item>
    <item name="android:textColor">#000000</item>
</style>
Bukunmi
fuente
1
el color del texto cambiaría en todas partes
user3156040
simplemente cambia el color de fondo de los elementos, y el diseño de las opciones de menú tiene relleno superior e inferior, no ayuda
FarshidABZ
10

¡Una cosa a tener en cuenta es que ustedes están complicando demasiado el problema al igual que muchas otras publicaciones! Todo lo que necesita hacer es crear selectores dibujables con los fondos que necesite y configurarlos como elementos reales. Pasé dos horas probando sus soluciones (todas sugeridas en esta página) y ninguna funcionó. Sin mencionar que hay toneladas de errores que esencialmente ralentizan su rendimiento en esos bloques try / catch que tiene.

De todos modos, aquí hay un archivo xml de menú:

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

Ahora en su 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>

La próxima vez que decida ir al supermercado a través de Canadá, pruebe Google Maps.

gotas de Jupiter
fuente
Estoy totalmente de acuerdo. ¿Por qué reinventar Android cuando él =) ya existe?
Fredrik
Funciona genial. Cree una lista de capas que se pueda dibujar con su icono y el fondo deseado. El único problema es que no sé si puedo cambiar el color del texto. Por lo tanto, no funciona cada color de fondo
Janusz
52
Hermoso, elegante y totalmente no aborda el problema.
Aea
1
Si no me equivoco, esto solo cambia el fondo del icono, no el elemento del menú en sí, que permanece en blanco.
J desde el
3
Esta no es una respuesta a la pregunta. Este es un pensamiento totalmente diferente.
Kostadin
4
 <style name="AppThemeLight" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:itemBackground">#000000</item>
</style>

Esto funciona bien para mi

Bebé Bincy
fuente
cómo cambiar el color del título
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;
                }
            });
        }
    }
Pappachan primigenio
fuente
3
Por favor, no haga esto: name.equalsIgnoreCase ("com.android.internal.view.menu.IconMenuItemView" Como su nombre lo indica claramente, esto está usando detalles de implementación privados y, por lo tanto, puede fallar en cualquier actualización de plataforma o dispositivo.
hackbod
1
IconMenuItemView es la clase que crea y controla el menú de opciones que se deriva de la clase Vista básica. Esta clase es del código fuente de Android y ha estado presente desde al menos la versión 5 de la API. No veo que se rompa en ninguna actualización de plataforma o dispositivo.
Primal Pappachan
1
No puedes verlo porque no puedes ver el futuro. Incluso si hubiera una forma de estar seguro, es una mala práctica
HXCaine
Gracias, esto es útil en caso de apuro. Sin embargo, no funciona en casos especiales, como los elementos creados en onCreateOptionsMenu, pero deshabilitados en onPrepareOptionsMenu, pero luego habilitados nuevamente.
HRJ
3

¡Gracias Marcus! Funciona en 2.3 sin problemas al corregir algunos errores de sintaxis, aquí está el código fijo

    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
fuente
1
Todo lo que obtengo por esto: java.lang.IllegalStateException: Ya se ha establecido una fábrica en este LayoutInflater
Bostone
para que funcione con ActionBarSherlock y el marco de compatibilidad y evitar IllegalStateException, vea este truco 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;
        }
    });
}

este es un archivo XML

gradient 
    android:startColor="#AFAFAF" 
    android:endColor="#000000"
    android:angle="270"
shape
Androide
fuente
1

Si desea establecer un color arbitrario, esto parece funcionar bastante bien para androidx. Probado en KitKat y Pie. Pon esto en tu 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);
}

Esto establece el color de android.widget.PopupWindow$PopupBackgroundView, que, como habrás adivinado, dibuja el color de fondo. No hay sobregiro y también puede usar colores semitransparentes.

ardilla
fuente