Android: ¿Cómo habilitar / deshabilitar el elemento del menú de opciones al hacer clic en el botón?

127

Puedo hacerlo fácilmente cuando estoy usando onCreateOptionsMenuo onOptionsItemSelectedmétodos.

Pero tengo un botón en algún lugar de la pantalla, y al hacer clic en ese botón, debería habilitar / deshabilitar los elementos del menú contextual.

Vikas
fuente

Respuestas:

272

De todos modos, la documentación cubre todas las cosas.

Cambiar elementos del menú en tiempo de ejecución

Una vez que se crea la actividad, el onCreateOptionsMenu()método se llama solo una vez, como se describió anteriormente. El sistema mantiene y reutiliza lo Menuque defina en este método hasta que se destruya su actividad. Si desea cambiar el menú de opciones en cualquier momento después de que se creó por primera vez, debe anular el onPrepareOptionsMenu()método. Esto le pasa el objeto Menú tal como existe actualmente. Esto es útil si desea eliminar, agregar, deshabilitar o habilitar elementos de menú según el estado actual de su aplicación.

P.ej

@Override
public boolean onPrepareOptionsMenu (Menu menu) {
    if (isFinalized) {
        menu.getItem(1).setEnabled(false);
        // You can also use something like:
        // menu.findItem(R.id.example_foobar).setEnabled(false);
    }
    return true;
}

En Android 3.0 y versiones posteriores, se considera que el menú de opciones siempre está abierto cuando los elementos del menú se presentan en la barra de acción. Cuando se produce un evento y desea realizar una actualización del menú, debe llamar invalidateOptionsMenu()para solicitar que el sistema llame onPrepareOptionsMenu().

Vikas
fuente
2
setEnable()cambia lo que sucede cuando presiona este menú, pero no cambia su aspecto (¿qué pasa, desarrolladores de Android?). Por lo tanto, es más claro deshabilitar y cambiar el título, o preferiblemente simplemente hacer lo MenuIteminvisible.
Vlad
19
Consejo rápido: regrese falsepara deshabilitar completamente el menú.
Bart Friederichs
55
Además, el comentario de la API sobre onPrepareOptionsMenu establece claramente: Las clases derivadas siempre deben (!) Llamar a la implementación de la clase base. Olvidaste tu súper llamada allí.
AgentKnopf
¿Cómo sabes la ID entera de MenuItem agregado en tiempo de ejecución? Solo puede agregarlos por nombre.
Antonio Sesto
66

En todas las versiones de Android, la forma más fácil: use esto para MOSTRAR un ícono de acción del menú como deshabilitado Y hacer que FUNCIÓN también esté deshabilitado:

@Override
public boolean onPrepareOptionsMenu(Menu menu) {

    MenuItem item = menu.findItem(R.id.menu_my_item);

    if (myItemShouldBeEnabled) {
        item.setEnabled(true);
        item.getIcon().setAlpha(255);
    } else {
        // disabled
        item.setEnabled(false);
        item.getIcon().setAlpha(130);
    }
}
Franco
fuente
1
¿Por qué establecer alfa en 0 cuando puede establecer la visibilidad en falso?
MLProgrammer-CiM
8
No estoy configurando alfa a 0, sino a 130.
Frank
8
Depende del contexto del botón. Si la función que el botón normalmente serviría es completamente inutilizable en el estado actual, entonces sí, la visibilidad debería ser invisible / desaparecida. Sin embargo, si la función que el botón normalmente serviría PODRÍA ser utilizable dadas las razones (por ejemplo, si el usuario se convierte en un miembro premium, si el usuario inicia sesión, si se conecta a Internet, etc.), entonces es bueno tenerlo en gris porque luego le permite al usuario saber que hay alguna característica existente a la que no tiene acceso por alguna razón.
yiati
66
Establecer el icono en invisible no es mejor en casi todos los casos de uso. En el caso de que tenga un grupo, es mejor que permanezcan en sus posiciones y que en gris indique al usuario que hay una opción que se puede habilitar en un determinado estado del programa. Hacer que las cosas sean invisibles e invisibles y probablemente causar cambios en el diseño es muy tonto. Hay una razón por la cual la mayoría de los menús oscurecen los elementos y no los eliminan según el modo.
RichieHH
1
¿Cómo si el menú no contiene icono? No puede establecer Alpha ().
Latief Anwar
42

Puede guardar el elemento como una variable al crear el menú de opciones y luego cambiar sus propiedades a voluntad.

private MenuItem securedConnection;
private MenuItem insecuredConnection;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.connect_menu, menu);
    securedConnection = menu.getItem(0);
    insecuredConnection =  menu.getItem(1);
    return true;
}

public void foo(){
       securedConnection.setEnabled(true);
}    
nir
fuente
Esa es la respuesta correcta a cualquier versión de Android. La respuesta validada solo es válida para dispositivos API 11>.
Marco HC
2
He intentado esto y no funciona. Quiso decir onPrepareOptionsMenu?
Christopher Pickslay
6

simplifique la versión de @Vikas

@Override
public boolean onPrepareOptionsMenu (Menu menu) {

    menu.findItem(R.id.example_foobar).setEnabled(isFinalized);
    return true;
}
Kosrat D. Ahmad
fuente
5

Cómo actualizar el menú actual para habilitar o deshabilitar los elementos cuando se realiza una AsyncTask.

En mi caso de uso, necesitaba deshabilitar mi menú mientras mi AsyncTask estaba cargando datos, luego, después de cargar todos los datos, necesitaba habilitar todo el menú nuevamente para permitir que el usuario lo usara.

Esto evitó que la aplicación permitiera a los usuarios hacer clic en los elementos del menú mientras se cargaban los datos.

Primero, declaro una variable de estado; si la variable es 0, se muestra el menú; si esa variable es 1, el menú está oculto.

private mMenuState = 1; //I initialize it on 1 since I need all elements to be hidden when my activity starts loading.

Luego en mi onCreateOptionsMenu()compruebo esta variable, si es 1, deshabilito todos mis elementos, si no, solo los muestro a todos

 @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.menu_galeria_pictos, menu);

        if(mMenuState==1){
            for (int i = 0; i < menu.size(); i++) {
                menu.getItem(i).setVisible(false);
            }
        }else{
             for (int i = 0; i < menu.size(); i++) {
                menu.getItem(i).setVisible(true);
            }
        }

        return super.onCreateOptionsMenu(menu);
    }

Ahora, cuando comienza mi actividad, onCreateOptionsMenu() se llamará solo una vez, y todos mis elementos desaparecerán porque configuré el estado para ellos al comienzo.

Luego creo una AsyncTask donde establezco esa variable de estado en 0 en mi onPostExecute()

¡Este paso es muy importante!

Cuando usted llama invalidateOptionsMenu(); se relanzaráonCreateOptionsMenu();

Entonces, después de configurar mi estado a 0, simplemente redibujé todo el menú, pero esta vez con mi variable en 0, dicho esto, todo el menú se mostrará después de que todo el proceso asíncrono se haya realizado, y luego mi usuario puede usar el menú .

 public class LoadMyGroups extends AsyncTask<Void, Void, Void> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mMenuState = 1; //you can set here the state of the menu too if you dont want to initialize it at global declaration. 

        }

        @Override
        protected Void doInBackground(Void... voids) {
           //Background work

            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);

            mMenuState=0; //We change the state and relaunch onCreateOptionsMenu
            invalidateOptionsMenu(); //Relaunch onCreateOptionsMenu

        }
    }

Resultados

ingrese la descripción de la imagen aquí

Gastón Saillén
fuente
2

la mejor solución cuando se realiza en el cajón de navegación

@Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        menu.setGroupVisible(0,false);
        return true;
    }
Prashant Gosai
fuente
Esto oculta todo el menú y hace que sea imposible abrirlo nuevamente
MrStahlfelge
2

Una respuesta más moderna para una vieja pregunta:

MainActivity.kt

private var myMenuIconEnabled by Delegates.observable(true) { _, old, new ->
    if (new != old) invalidateOptionsMenu()
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    findViewById<Button>(R.id.my_button).setOnClickListener { myMenuIconEnabled = false }
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.menu_main_activity, menu)
    return super.onCreateOptionsMenu(menu)
}

override fun onPrepareOptionsMenu(menu: Menu): Boolean {
    menu.findItem(R.id.action_my_action).isEnabled = myMenuIconEnabled
    return super.onPrepareOptionsMenu(menu)
}

menu_main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
    android:id="@+id/action_my_action"
    android:icon="@drawable/ic_my_icon_24dp"
    app:iconTint="@drawable/menu_item_icon_selector"
    android:title="My title"
    app:showAsAction="always" />
</menu>

menu_item_icon_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?enabledMenuIconColor" android:state_enabled="true" />
<item android:color="?disabledMenuIconColor" />

attrs.xml

<resources>   
    <attr name="enabledMenuIconColor" format="reference|color"/>
    <attr name="disabledMenuIconColor" format="reference|color"/>
</resources>

styles.xml or themes.xml

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="disabledMenuIconColor">@color/white_30_alpha</item>
    <item name="enabledMenuIconColor">@android:color/white</item>
CaroBelly
fuente
Gran enfoque que se puede usar sin mucha repetitiva en toda la aplicación.
Arthur
¿Qué problema estás teniendo?
ExpensiveBelly
@ExpensiveBelly no es un problema per se, solo un aviso público.
Big McLargeHuge
1
  @Override
        public boolean onOptionsItemSelected(MenuItem item) {

            switch (item.getItemId()) {

                case R.id.item_id:

                       //Your Code....

                        item.setEnabled(false);
                        break;
              }
            return super.onOptionsItemSelected(item);
     }
Tousif Akram
fuente
De la opinión: Hola, por favor no responda solo con el código fuente. Intente proporcionar una buena descripción sobre cómo funciona su solución. Ver: ¿Cómo escribo una buena respuesta? . Gracias
sɐunıɔ ןɐ qɐp
1

Lo que hice fue guardar una referencia al Menú en onCreateOptionsMenu. Esto es similar a la respuesta de nir, excepto que en lugar de guardar cada elemento individual, guardé todo el menú.

Declarar un menú Menu toolbarMenu;.

Luego en onCreateOptionsMenuguardar el menú a su variable

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    getMenuInflater().inflate(R.menu.main_menu, menu);
    toolbarMenu = menu;
    return true;
}

Ahora puede acceder a su menú y a todos sus elementos en cualquier momento que desee. toolbarMenu.getItem(0).setEnabled(false);

Smitty-Werben-Jager-Manjenson
fuente
0
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    // getMenuInflater().inflate(R.menu.home, menu);
    return false;
}
Draken
fuente
2
Si bien este código puede responder a la pregunta del OP, unas pocas palabras de explicación ayudarán a los usuarios actuales y futuros a comprender aún mejor su respuesta.
Thom
0

Si menú visible

menu.findItem(R.id.id_name).setVisible(true);

Si ocultar menú

menu.findItem(R.id.id_name).setVisible(false);
murugan mani
fuente
-4

En general, puede cambiar las propiedades de sus vistas en tiempo de ejecución:

(Button) item = (Button) findViewById(R.id.idBut);

y entonces...

item.setVisibility(false)

pero

si desea modificar la visibilidad de las opciones desde ContextMenu, al presionar su botón, puede activar una bandera, y luego en onCreateContextMenu puede hacer algo como esto:

 @Override
        public void onCreateContextMenu(ContextMenu menu, 
                View v,ContextMenu.ContextMenuInfo menuInfo) {

            super.onCreateContextMenu(menu, v, menuInfo);

                menu.setHeaderTitle(R.string.context_title);

                if (flagIsOn()) {
                    addMenuItem(menu, "Option available", true);
                } else {
                    Toast.makeText(this, "Option not available", 500).show();
                }

        }

espero que esto ayude

Eric
fuente
Debo decir que te estás equivocando, quiero que se deshabilite el elemento del menú, no el botón.
Vikas
Mi respuesta está completa. Este código funciona, lo he usado en mis proyectos
Eric
1
Bueno, gracias por el trabajo, pero deberías leer correctamente la pregunta que ya dije que puedo cambiarla en el onCreateContextMenumétodo. Pero quiero acceder al menú contextual desde este método.
Vikas
onCreateContextMenuse llamará solo una vez, pero puedo hacer clic en el botón muchas veces para habilitar / deshabilitar el elemento del menú.
Vikas
Sí, pero el menú contextual normalmente está oculto. Si presiona su 'botón en algún lugar' y establece la bandera como dije, este menú contextual no está visible y la próxima vez que vuelva a cargar su menú contextual, esta opción será invisible. Otra opción es hacer otro tipo de menú con la misma apariencia para manejar los eventos con otros métodos.
Eric