En Android podemos encontrar 3 tipos diferentes de
menús:
·
Menús Principales.
Los más habituales, aparecen en la zona inferior de la pantalla al pulsar el
botón ‘menu’ del teléfono.
·
Submenús.
Son menús secundarios que se pueden mostrar al pulsar sobre una opción de un
menú principal.
·
Menús Contextuales.
Útiles en muchas ocasiones, aparecen al realizar una pulsación larga sobre
algún elemento de la pantalla.
Como casi siempre, vamos a tener dos alternativas a la
hora de mostrar un menú en nuestra aplicación Android. La primera de ellas
mediante la definición del menú en un archivo XML, y la segunda creando el menú
directamente mediante código. Estudiemos ambas alternativas.
Veamos en primer
lugar cómo crear un menú a partir de su definición en XML. Los archivos XML de
menú se deben colocar en la carpeta “res\menu” de nuestro proyecto
y tendrán una estructura análoga a la del siguiente ejemplo:
|
<?xml version="1.0" encoding="utf-8"?>
<menu
<item android:id="@+id/MnuOpc1" android:title="Opcion1"
android:icon="@drawable/tag"></item>
<item android:id="@+id/MnuOpc2" android:title="Opcion2"
android:icon="@drawable/filter"></item>
<item android:id="@+id/MnuOpc3" android:title="Opcion3"
android:icon="@drawable/chart"></item>
</menu>
|
Como vemos, la
estructura básica de estos archivos es muy sencilla. Tendremos un elemento
principal <menu> que contendrá una serie de
elementos <item> que se corresponderán con las
distintas opciones a mostrar en el menú. Estos elementos <item> tendrán a
su vez varias propiedades básicas, como su ID (android:id),
su texto (android:title) o su icono (android:icon).
Los iconos utilizados deberán estar por supuesto en las carpetas “res\drawable-…”
de nuestro proyecto.
Una vez definido el
menú en el archivo XML, tendremos que implementar el evento onCreateOptionsMenu() de la actividad que queremos que
lo muestre. En este evento deberemos “inflar”
el menú de forma parecida a cómo ya hemos hecho otras veces con otro tipo de
layouts. Primero obtendremos una referencia al inflater mediante
el método getMenuInflater() y posteriormente generaremos la
estructura del menú llamando a su método infate() pasándole como parámetro el ID
del menu definido en XML, que en nuestro caso será R.menu.menu_principal.
Por último devolveremos el valor true para confirmar que debe mostrarse
el menú.
|
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_principal, menu);
return true;
}
|
Y ya hemos terminado, con estos sencillos pasos
nuestra aplicación ya debería mostrar sin problemas el menú que hemos
construido, aunque todavía nos faltaría implementar la funcionalidad de cada
una de las opciones mostradas.
Como hemos comentado
antes, este mismo menú también lo podríamos crear directamente mediante código,
también desde el evento onCreateOptionsMenu().
Para esto, para agregar cada opción del menú podemos utilizar el método add() sobre
el objeto de tipo Menu que nos llega como parámetro del
evento. Este método recibe 4 parámetros: ID del grupo asociado a la opción ( por ahora utilizaremos Menu.NONE), un ID único para
la opción (que declararemos como constantes de la clase), el orden de la opción
(que no nos interesa por ahora, utilizaremos Menu.NONE) y
el texto de la opción. Por otra parte, el icono de cada opción lo
estableceremos mediante el método setIcon() pasándole el ID del recurso.
Veamos cómo quedaría el código utilizando esta alternativa, que generaría un
menú exactamente igual al del ejemplo anterior:
|
private static final int MNU_OPC1 = 1;
private static final int MNU_OPC2 = 2;
private static final int MNU_OPC3 = 3;
//...
@Override
public boolean onCreateOptionsMenu(Menu
menu) {
//agregamos al menu
menu.add(Menu.NONE, MNU_OPC1, Menu.NONE,
"Opcion1").setIcon(R.drawable.tag);
menu.add(Menu.NONE, MNU_OPC1, Menu.NONE,
"Opcion2").setIcon(R.drawable.filter);
menu.add(Menu.NONE, MNU_OPC1, Menu.NONE,
"Opcion3").setIcon(R.drawable.chart);
return true;
}
|
Construido el menú,
la implementación de cada una de las opciones se incluirá en el evento onOptionsItemSelected() de la actividad que mostrará el
menú. Este evento recibe como parámetro el item de menú que ha sido pulsado por
el usuario, cuyo ID podemos recuperar con el método getItemId(). Según este ID
podremos saber qué opción ha sido pulsada y ejecutar unas acciones u otras. En
nuestro caso de ejemplo, lo único que haremos será modificar el texto de una
etiqueta (lblMensaje) colocada en la
pantalla principal de la aplicación.
|
@Override
public boolean onOptionsItemSelected(MenuItem
item) {
switch (item.getItemId()) {
case R.id.MnuOpc1:
lblMensaje.setText("Opcion
1 pulsada!");
return true;
case R.id.MnuOpc2:
lblMensaje.setText("Opcion
2 pulsada!");;
return true;
case R.id.MnuOpc3:
lblMensaje.setText("Opcion
3 pulsada!");;
return true;
default:
return super.onOptionsItemSelected(item);
}
}
|
Con esto, hemos
conseguido ya un menú completamente funcional. Si ejecutamos el proyecto en el
emulador comprobaremos cómo al pulsar el botón de ‘menu‘ del teléfono
aparece el menú que hemos definido y que al pulsar cada opción se muestra el
mensaje de ejemplo.
Pasemos ahora a
comentar los submenús.
Un submenú no es más que un menú secundario que se muestra al pulsar una opción
determinada de un menú principal. Los submenús en Android se muestran en forma
de lista emergente, cuyo título contiene el texto de la opción elegida en el
menú principal. Como ejemplo, vamos a agregar un submenú a la Opción 3 del
ejemplo anterior, al que añadiremos dos nuevas opciones secundarias. Para ello,
bastará con insertar en el XML de menú un nuevo elemento <menu> dentro
del item correspondiente a la opción 3. De esta forma, el XML quedaría ahora
como sigue:
|
<?xml version="1.0" encoding="utf-8"?>
<menu
<item android:id="@+id/MnuOpc1" android:title="Opcion1"
android:icon="@drawable/tag"></item>
<item android:id="@+id/MnuOpc2" android:title="Opcion2"
android:icon="@drawable/filter"></item>
<item android:id="@+id/MnuOpc3" android:title="Opcion3"
android:icon="@drawable/chart">
<menu>
<item android:id="@+id/SubMnuOpc1"
android:title="Opcion
3.1" />
<item android:id="@+id/SubMnuOpc2"
android:title="Opcion
3.2" />
</menu>
</item>
</menu>
|
Si volvemos a ejecutar ahora el proyecto y pulsamos la
opción 3 nos aparecerá el correspondiente submenú con las dos nuevas opciones agregadas.
Lo vemos en la siguiente imagen:
Comprobamos como
efectivamente aparecen las dos nuevas opciones en la lista emergente, y que el
título de la lista se corresponde con el texto de la opción elegida en el menú
principal (“Opcion3“).
Para conseguir esto
mismo mediante código procederíamos de forma similar a la anterior, con la
única diferencia de que la opción de menú 3 la agregaremos utilizando el método addSubMenu() en
vez de add(), y
guardando una referencia al submenu. Sobre el submenú agregado insertaremos las
dos nuevas opciones utilizando una vez más el método add(). Vemos cómo
quedaría:
|
// agregamos al menu
menu.add(Menu.NONE, MNU_OPC1,
Menu.NONE, "Opcion1").setIcon(R.drawable.tag);
menu.add(Menu.NONE, MNU_OPC1,
Menu.NONE, "Opcion2").setIcon(R.drawable.filter);
//menu.add(Menu.NONE, MNU_OPC1,
Menu.NONE, "Opcion3").setIcon(R.drawable.chart);
SubMenu smnu =
menu.addSubMenu(Menu.NONE, MNU_OPC1, Menu.NONE, "Opcion3")
.setIcon(R.drawable.chart);
smnu.add(Menu.NONE, SMNU_OPC1,
Menu.NONE, "Opcion 3.1");
smnu.add(Menu.NONE, SMNU_OPC2, Menu.NONE, "Opcion 3.2");
|
En cuanto a la
implementación de estas opciones de submenú no habría diferencia con todo lo
comentado anteriormente ya que también se tratan desde el evento onOptionsItemSelected(),
identificándolas por su ID.
Menús Contextuales
Existe otro tipo de menús que nos pueden ser muy
útiles en determinados contextos: los menús contextuales. Este tipo de menú
siempre va asociado a un control concreto de la pantalla y se muestra al
realizar una pulsación larga sobre éste. Suele mostrar opciones específicas
disponibles únicamente para el elemento pulsado. Por ejemplo, en un control de
tipo lista podríamos tener un menú contextual que apareciera al pulsar sobre un
elemento concreto de la lista y que permitiera editar su texto o eliminarlo de
la colección.
Bueno, la creación y utilización de este tipo de menús
es muy parecida a lo que ya vimos para los menús y submenús básicos, pero
presentan algunas cosas que hacen interesante tratarlos al margen del resto.
Empecemos por un caso
sencillo. Vamos a partir del ejemplo anterior, al que vamos a agregarle en
primer lugar un menú contextual que aparezca al pulsar sobre la etiqueta de
texto que mostrábamos en la ventana principal de la aplicación. Para esto, lo
primero que vamos a hacer es indicar en el método onCreate() de nuestra actividad principal
que la etiqueta tendrá asociado un menú contextual. Esto lo conseguimos con una
llamada a registerForContextMenu():
|
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Obtenemos las
referencias a los controles
lblMensaje
= (TextView)findViewById(R.id.LblMensaje);
//Asociamos
los menús contextuales a los controles
registerForContextMenu(lblMensaje);
}
|
A continuación, igual
que hacíamos con onCreateOptionsMenu() para los menús básicos, vamos a
sobreescribir en nuestra actividad el evento encargado de construir los menús
contextuales asociados a los diferentes controles de la aplicación. En este
caso el evento se llama onCreateContextMenu(), y a diferencia de onCreateOptionsMenu() éste se llama cada vez que se
necesita mostrar un menú contextual, y no una sola vez al inicio de la
aplicación. En este evento actuaremos igual que para los ménus básicos, inflando el
menú XML que hayamos creado con las distintas opciones, o creando a mano el
menú mediante el método add(). En nuestro ejemplo hemos definido
un menú en XML llamado “menu_ctx_etiqueta.xml“:
|
<?xml version="1.0" encoding="utf-8"?>
<menu
<item android:id="@+id/CtxLblOpc1"
android:title="OpcEtiqueta1"></item>
<item android:id="@+id/CtxLblOpc2"
android:title="OpcEtiqueta2"></item>
</menu>
|
Por su parte el evento onCreateContextMenu() quedaría
de la siguiente forma:
|
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo
menuInfo)
{
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_ctx_etiqueta,
menu);
}
|
Por último, para
implementar las acciones a realizar tras pulsar una opción determinada del menú
contextual vamos a implementar el evento onContextItemSelected() de forma análoga a cómo hacíamos
con onOptionsItemSelected() para los menús básicos:
|
@Override
public boolean onContextItemSelected(MenuItem
item) {
switch (item.getItemId()) {
case R.id.CtxLblOpc1:
lblMensaje.setText("Etiqueta: Opcion 1
pulsada!");
return true;
case R.id.CtxLblOpc2:
lblMensaje.setText("Etiqueta: Opcion 2
pulsada!");
return true;
default:
return super.onContextItemSelected(item);
}
}
|
Con esto, ya tendríamos listo nuestro menú contextual
para la etiqueta de texto de la actividad principal, y como vemos todo es
prácticamente igual a cómo construimos los menús y submenús básico. En este
punto ya podríamos ejecutar el proyecto en el emulador y comprobar su
funcionamiento.
Ahora vamos con
algunas particularidades. Los menús contextuales se utilizan a menudo con
controles de tipo lista, lo que agregan algunos detalles que conviene
mencionar. Para ello vamos a agregar a nuestro ejemplo una lista con varios
datos de muestra y vamos a asociarle un nuevo menú contextual. Modificaremos el
layout XML de la ventana principal para agregar el control ListView y
modificaremos el método onCreate() para obtener la referencia al
control, insertar varios datos de ejemplo, y asociarle un menú contextual:
|
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Obtenemos las
referencias a los controles
lblMensaje
= (TextView)findViewById(R.id.LblMensaje);
lstLista
= (ListView)findViewById(R.id.LstLista);
//Llenamos
la lista con datos de ejemplo
String[]
datos =
new String[]{"Elem1","Elem2","Elem3","Elem4","Elem5"};
ArrayAdapter<String> adaptador =
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
datos);
lstLista.setAdapter(adaptador);
//Asociamos
los menús contextuales a los controles
registerForContextMenu(lblMensaje);
registerForContextMenu(lstLista);
}
|
Como en el caso
anterior, vamos a definir en XML otro menú para asociarlo a los elementos de la
lista, lo llamaremos “menu_ctx_lista“:
|
<?xml version="1.0" encoding="utf-8"?>
<menu
<item android:id="@+id/CtxLstOpc1"
android:title="OpcLista1"></item>
<item android:id="@+id/CtxLstOpc2"
android:title="OpcLista2"></item>
</menu>
|
Como siguiente paso,
y dado que vamos a tener varios menús contextuales en la misma actividad,
necesitaremos modificar el evento onCreateContextMenu() para que se construya un menú
distinto dependiendo del control asociado. Esto lo haremos obteniendo el ID del
control al que se va a asociar el menú contextual, que se recibe en forma de
parámetro (View v) en el evento onCreateContextMenu().
Utilizaremos para esto una llamada al método getId() de dicho parámetro:
|
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo
menuInfo)
{
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
if(v.getId() == R.id.LblMensaje)
inflater.inflate(R.menu.menu_ctx_etiqueta,
menu);
else if(v.getId() == R.id.LstLista)
{
AdapterView.AdapterContextMenuInfo
info =
(AdapterView.AdapterContextMenuInfo)menuInfo;
menu.setHeaderTitle(
lstLista.getAdapter().getItem(info.position).toString());
inflater.inflate(R.menu.menu_ctx_lista, menu);
}
}
|
Vemos cómo en el caso
del menú para el control lista hemos ido además un poco más allá, y hemos
personalizado el título del menú contextual [mediante setHeaderTitle()]
para que muestre el texto del elemento seleccionado en la lista. Para hacer
esto nos hace falta saber la posición en la lista del elemento seleccionado,
algo que podemos conseguir haciendo uso del último parámetro recibido en el
evento onCreateContextMenu(),
llamado menuInfo.
Este parámetro contiene información adicional del control que se ha pulsado
para mostrar el menú contextual, y en el caso particular del control ListView contiene
la posición del elemento concreto de la lista que se ha pulsado. Para
obtenerlo, convertimos el parámetro menuInfo a un objeto de tipo AdapterContextMenuInfo y accedemos a su atributo position tal
como vemos en el código anterior.
La respuesta a este
nuevo menú se realizará desde el mismo evento que el anterior, todo dentro de onContextItemSelected().
Por tanto, incluyendo las opciones del nuevo menú contextual para la lista el
código nos quedaría de la siguiente forma:
|
@Override
public boolean onContextItemSelected(MenuItem
item) {
AdapterContextMenuInfo info =
(AdapterContextMenuInfo)
item.getMenuInfo();
switch (item.getItemId()) {
case R.id.CtxLblOpc1:
lblMensaje.setText("Etiqueta: Opcion 1
pulsada!");
return true;
case R.id.CtxLblOpc2:
lblMensaje.setText("Etiqueta: Opcion 2
pulsada!");
return true;
case R.id.CtxLstOpc1:
lblMensaje.setText("Lista[" + info.position + "]: Opcion 1 pulsada!");
return true;
case R.id.CtxLstOpc2:
lblMensaje.setText("Lista[" + info.position + "]: Opcion 2 pulsada!");
return true;
default:
return super.onContextItemSelected(item);
}
}
|
Como vemos, aquí
también utilizamos la información del objeto AdapterContextMenuInfo para saber qué elemento de la
lista se ha pulsado, con la única diferencia de que en esta ocasión lo
obtenemos mediante una llamada al método getMenuInfo() de la opción de menú (MenuItem)
recibida como parámetro.
Si volvemos a ejecutar el proyecto en este punto
podremos comprobar el aspecto de nuestro menú contextual al pulsar cualquier
elemento de la lista:
A modo de resumen, hemos estudiado cómo crear menús
contextuales asociados a determinados elementos y controles de nuestra interfaz
de la aplicación. Hemos visto cómo crear menús básicos y algunas
particularidades que existen a la hora de asociar menús contextuales a
elementos de un control de tipo lista
Copy paste, te podrías molestar al menos en cambiar los ejemplos... Para eso mejor no hacer nada ;)
ResponderEliminar