item - android style generator
¿Cómo cambiar el color de fondo del menú de opciones? (11)
¡Una cosa para notar que ustedes están complicando demasiado el problema al igual que muchos otros mensajes! Todo lo que necesita hacer es crear selectores dibujables con los fondos que necesite y configurarlos para elementos reales. Solo paso dos horas probando tus soluciones (todas sugeridas en esta página) y ninguna funcionó. Sin mencionar que hay toneladas de errores que esencialmente disminuyen tu rendimiento en esos bloques try / catch que tienes.
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 los mapas de Google!
Estoy intentando cambiar el color predeterminado para el menú de opciones que es blanco: quiero un fondo negro para cada elemento en el menú de opciones.
He intentado algunos brotes como android: itemBackground = "# 000000" en el elemento del elemento del menú, pero no funcionó.
¿Cómo puedo lograr esto?
Acabo de toparme con este problema también, en una aplicación que tenía que ser compatible con Gingerbread y todavía conserva la mayor parte posible del estilo de los dispositivos con Holo.
Encontré una solución relativamente limpia, que funcionó bien para mí.
En el tema utilizo 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 el estilo del color del texto, y simplemente usé un Spannable para establecer el color del texto para 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;
}
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>
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 desde API 4.2 a 5.0.
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/black
o @drawable/my_drawable
), se bloqueará si usa un valor de color directamente.
Esto también eliminará los bordes del artículo que no pude cambiar o eliminar usando la solución de primalpop.
En cuanto al color del texto, no he encontrado ninguna manera de configurarlo a través de los estilos en 2.2 y estoy seguro de que lo he intentado todo (que es así como descubrí el atributo de fondo del menú). Necesitarías usar la solución de primalpop para eso.
Este es claramente un problema que tienen muchos programadores y al que Google aún debe proporcionar una solución satisfactoria y compatible.
Hay muchas intenciones cruzadas y malentendidos que flotan alrededor de publicaciones sobre este tema, así que lea esta respuesta completa antes de responder.
A continuación, incluyo una versión más refinada y mejor comentada del hack 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: personalizar el menú de la aplicación (por ejemplo, color de fondo)
http://www.macadamian.com/blog/post/android_-_theming_the_unthemable/
Botón de alternar MenuItem de Android
¿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 hack en 2.1 (simulador), 2.2 (2 dispositivos reales) y 2.3 (2 dispositivos reales). No tengo tabletas 3.X para probar todavía, pero publicaré los cambios necesarios aquí si lo hago. 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
este truco casi seguro no hará nada (sin daños ni beneficios) en las tabletas 3.X.
DECLARACIÓN DEL PROBLEMA (lea esto antes de responder a un disparador con un comentario negativo):
El menú de 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 también el color del texto del menú de Opciones .
Ciertos 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 de android: panelFullBackground descrito en otra respuesta. Sin embargo, actualmente no hay forma de controlar el color del texto del menú Opciones con los estilos, por lo que solo se puede usar este método para cambiar el fondo a otro color que no hará que el texto "desaparezca".
Nos encantaría hacer esto con una solución documentada, a prueba de futuro, pero una simplemente no está disponible desde Android <= 2.3. Entonces, tenemos que usar una solución que funcione en las versiones actuales y esté diseñada para minimizar las posibilidades de fallas / fallas en futuras versiones. Queremos una solución que falle correctamente 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 (por lo general, para que coincida con un estilo visual para el resto de la aplicación), por lo que no insistiré en eso.
Hay un error de Google Android publicado sobre esto: por favor agregue su apoyo al protagonizar 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 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 dio lugar a una NullPointerException.
Mi truco un poco 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 de Java). No veo ninguna forma de evitar esto y aún lograr el objetivo establecido. Sin embargo, es posible hacer el hack de una manera cuidadosa que retrocederá si "com.android.internal.view.menu.IconMenuItemView" no aparece en el sistema actual.
De nuevo, entiendo que esto es un truco y de ninguna manera estoy afirmando que esto funcionará en todas las plataformas. Pero nosotros, los desarrolladores, no vivimos en un mundo académico de fantasía donde todo tiene que ser según el libro: 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 las tabletas 3.X ya que usan barras de acción en lugar de menús de opciones.
Finalmente, algunos desarrolladores han resuelto este problema al suprimir por completo el menú de opciones de Android y escribir su propia clase de menú (ver algunos de los enlaces anteriores). No he intentado esto, pero si tienes tiempo para escribir tu 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 piratas indocumentados.
CORTAR:
Aquí está el código.
Para usar este código, llame a addOptionsMenuHackerInflaterFactory () UNA VEZ desde su actividad onCreate () o su actividad en CreateOptionsMenu (). Establece una fábrica predeterminada que afectará la creación posterior de cualquier Menú de Opciones. No afecta a los menús de opciones que ya se han creado (los hacks anteriores usaban un nombre de función de setMenuBackground (), lo cual es muy engañoso ya que la función no establece ninguna propiedad de menú antes de que vuelva).
@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!
Gracias Marcus! Funciona en 2.3 sin problemas mediante la reparación de 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
}
}
Para Android 2.3 esto se puede hacer con algunos hackeos muy pesados:
La causa principal de los problemas con Android 2.3 es que en LayoutInflater, mConstructorArgs [0] = mContext solo se establece durante la ejecución de llamadas a
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
}
}
(Siéntase libre de votar por esta respuesta;)) 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
/*
*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;
}
});
}
}
<style name="AppThemeLight" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:itemBackground">#000000</item>
</style>
Esto funciona bien para mi
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