Академический Документы
Профессиональный Документы
Культура Документы
Tema 9. Menús y Action Bar
Primera parte
Introducción
Los menús son una parte importante de la interfaz de usuario de las actividades ya que
proporcionan una manera intuitiva de realizar acciones.
Android provee un conjunto de clases (un framework) que permitirá añadir menús a la
aplicación a desarrollar.
Existen tres tipos de menús:
Menú de Opciones. Es una colección de ítems asignados para cada actividad, la cual
aparecerá al pulsar la tecla menú del dispositivo. A partir de Android 3.0, se pueden
crear accesos directos de estos ítems en la Action Bar¸ convirtiéndose dichos ítmes en
action items.
Submenú. Es una lista flotante de opciones que aparece cuando el usuario selecciona
un ítem de menú que contiene otro menú anidado.
Menú Contextual. Es una lista flotante de opciones que puede aparecer cuando el
usuario pulsa de forma mantenida sobre una actividad o componente de la aplicación.
En esta lección veremos cómo crear menús dentro de la aplicación que desarrollemos
utilizando XML para definir su contenido, y cómo implementar métodos en la actividad que
capturen las selecciones de ítems de menú que el usuario realice.
Aunque es posible crear menús vía código en una actividad, no es una buena praxis, puesto
que se trata de un componente visual y, como tal, debe ser definido vía XML. Los menús, así
como todos sus ítems (visibles o no inicialmente), se definirán en la carpeta “res/menu/” en
archivos XML. Existirá un archivo XML por menú de la aplicación, cuyo nombre será el
identificador que utilizará la clase R para hacerlo accesible desde código.
La estructura de un menú se construye utilizando tres etiquetas XML: <menu>, <item> y
<group>. Cada etiqueta podrá contener diversos atributos, siendo los más comunes:
android:id, android:title o android:icon1. Los iconos que se deseen utilizar deberán estar, igual
que el resto de recusos gráficos accesibles desde código, en la carpeta “res/drawable‐*/”.
En cada archivo XML de menú, el elemento raíz será siempre un elemento <menu>, que
representará un objeto tipo Menu en el código, y contendrá uno o más elementos <item> y
<group>.
1
Existen más atributos asociados a la etiqueta <item>, incluyendo algunos que especifican cuál sería el aspecto del
ítem en la Action Bar.
El elemento <item>, representará un objeto MenuItem en el código, y podrá contener, a su
vez, un elemento anidado de tipo <menu> para definir un submenú.
Por último, el elemento <group> es un elemento invisible que se utilizará para agrupar
elementos <item> con el objeto de asignar al grupo de ítems propiedades comunes tales como
su visibilidad o su estado (activo o inactivo).
MenuInflater
Para convertir la fuente XML en un objeto Java, se utiliza la clase MenuInflater (que infla el
menú XML convirtiéndolo en un objeto menú). Se deberá implementar el método
onCreateOptionsMenu() en aquella actividad en la que se quiera asociar un menú de
opciones. Este método será invocado la primera vez que se solicite el menú, en dispositivos
con Android 2.3 o inferior, mientras que en dispositivos con Android 3.0 o superior, se invocará
en el momento en el que la actividad sea creada, con el objeto de poblar la Action Bar (visible
desde la creación de la actividad).
En nuestro caso, para convertir a objeto el menú de ejemplo, el código del método será el
siguiente (nótese que para acceder al archivo XML se utiliza R.menu.nombre_archivo):
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_musica, menu);
return true;
}
Cuando el usuario pulsa un ítem de un menú de opciones, el sistema invoca en la actividad al
método onOptionsItemSelected(), método que recibe como parámetro el MenuItem
seleccionado. Como cualquier elemento definido en la interfaz de usuario se identifica
unívocamente por su id (android:id, en el XML), se podrá determinar qué ítem ha pulsado,
invocando al método getItemId(), y lanzar una u otra acción en consecuencia.
El método onOptionsItemSelected() devolverá true en el caso de que gestione
correctamente el ítem pulsado, y false en caso contrario.
A partir de Android 3.0, existe la opción de asignar un evento tipo onClick a cada ítem del
menú de forma que al pulsar un ítem, se invoque a un método concreto. Esto se consigue
usando el atributo android:onClick y especificando el método que invocará.
Tal y como se ha comentado más arriba, las mejores prácticas de programación establecen que
el diseño del menú radique en un archivo XML. Sin embargo, en ciertas ocasiones, será
necesario añadir o eliminar ítems del menú, activarlos, desactivarlos o modificarlos.
A partir de Android 3.0, y como la Action Bar (análogo al menú) se muestra siempre, para
poder modificar los ítems del menú se deberá invocar previamente a
invalidateOptionsMenu(). A continuación, el sistema invocará a
onPrepareOptionsMenu().
Submenús
Tal y como se ha explicado más arriba, cada elemento <item> podrá contener un elemento
anidado de tipo <menu> para definir un submenú. Del mismo modo que en el caso de los ítems
de menú, cuando el usuario pulse sobre una opción del submenú se invocará al método
onOptionsItemSelected() del menú de opciones.
Ejemplo 1. Menú Música!
Para aplicar los conceptos explicados hasta este punto, se va a implementar un menú que
contiene cuatro ítems de una hipotética aplicación para buscar, escuchar, grabar música y
elegir el formato de grabación, y que será guardado en el archivo menu_musica.xml. Además,
al pulsar la opción “Buscar” la aplicación mostrará un submenú con otras tres opciones:
“Grabaciones”, “Música en Móvil” y “Música en Internet”.
Se ha implementado una modificación en tiempo de ejecución el cuarto ítem del menú de
forma que, si es pulsado, se cambia la imagen y el texto del mismo.
Código de menu_musica.xml
<?xml version="1.0" encoding="utf‐8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/buscar"
android:title="@string/buscar"
android:icon="@drawable/note">
<menu>
<item android:id="@+id/grabaciones"
android:title="@string/grabaciones" />
<item android:id="@+id/movil"
android:title="@string/musica_movil" />
<item android:id="@+id/internet"
android:title="@string/musica_internet" />
</menu>
</item>
<item
android:id="@+id/escuchar"
android:title="@string/escuchar"
android:icon="@drawable/listen"></item>
<item
android:id="@+id/grabar"
android:title="@string/grabar"
android:icon="@drawable/mic"></item>
<item
android:id="@+id/cd_vinilo"
android:title="@string/vinilo"
android:icon="@drawable/vinilo"></item>
</menu>
Código de EditorMusicaActivity.java (versión 1)
package com.especialcursos.menus;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
public class EditorMusicaActivity extends Activity {
static int CD_VINILO = 0;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_musica, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Se gestiona la opción seleccionada
switch (item.getItemId()) {
case R.id.buscar:
buscar();
return true;
case R.id.escuchar:
escuchar();
return true;
case R.id.grabar:
grabar();
return true;
case R.id.cd_vinilo:
cd_vinilo();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// Método invocado inmediatamente antes de mostrarse el menú, cada vez.
// Recibe como parámetro el Menu, con la estructura tal y como esté justo
// antes de ser mostrado.
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (CD_VINILO == 1) {
menu.findItem(R.id.cd_vinilo).setIcon(R.drawable.cd);
menu.findItem(R.id.cd_vinilo).setTitle(R.string.cd);
}
else {
menu.findItem(R.id.cd_vinilo).setIcon(R.drawable.vinilo);
menu.findItem(R.id.cd_vinilo).setTitle(R.string.vinilo);
}
// return false: no muestra menú
// return true: se muestra menú.
// Buena práctica: delegar en clase padre.
return super.onPrepareOptionsMenu(menu);
}
private void cd_vinilo() {
if (CD_VINILO == 0)
CD_VINILO = 1;
else
CD_VINILO = 0;
}
private void buscar() {
// TODO
}
private void escuchar() {
// TODO
}
private void grabar() {
// TODO
}
}
Construcción de un menú contextual
Los menús contextuales se utilizan para proporcionar acceso a acciones al mantener pulsado
un elemento de la aplicación. Se puede añadir un menú contextual a cualquier elemento tipo
vista (View), siendo lo más común añadirlos en elementos del tipo ListView.
Para que una vista proporcione un menú contextual es necesario registrarla con el método
registerForContextMenu(), método al cual habrá que pasar, como parámetro, la propia
vista.
Del mismo modo que en el caso de los menús de opciones, para gestionar menús contextuales
se sobreescribirán los métodos onCreateContextMenu() y onContextItemSelected().
Mientras que el segundo método recibe un MenuItem como parámetro, el primer método
recibe el ContextMenu, la vista que muestra el menú contextual y un ContextMenuInfo, el
cual proporcionará información adicional sobre el ítem que se seleccione.
Para crear el menú contextual, se deberá utilizar un MenuInflater al igual que en el caso de
los menús de opciones.
A continuación, ampliaremos funcionalidades en la aplicación que se ha comenzado a
desarrollar en el Ejemplo 1, con el propósito de implementar un menú contextual sobre una
vista tipo ListView, objeto muy importante y versátil que provee el framework Android.
Una de las funcionalidades que ya implementa la vista ListView es la posibilidad de habilitar
un filtro de texto en función de lo que escriba el usuario e implementar un listener para cada
ítem de la lista, de forma que puedan realizarse acciones sobre dichos ítems.
En la aplicación de ejemplo, se utilizará una segunda actividad que herede de ListActivity y
que sea invocada por Intent (tal y como ya se vio en el Tema 4). El Intent será creado al
pulsar sobre la opción del menú “Escuchar” o sobre cualquiera de las opciones del submenú
“Buscar”. Esta nueva actividad, ResultadosMusicaActivity.java, mostrará una lista de
grupos musicales que será cargada desde el fichero strings.xml. A la lista, de tipo ListView se
le asignará un menú contextual con tres opciones (“Escuchar”, “Enviar” y “Borrar”). Además, a
la lista se le asignará el listener del evento onItemClick de forma que, cuando se pulse sobre un
elemento, se muestre un pequeño mensaje al usuario (o Toast).
Nótese que, debido a que esta lista es un objeto tipo ListView, admite filtrados por texto.
Mantener pulsado
Pulsación sencilla
Filtro. Pulsación tecla “N”
Toast
Código de EditorMusicaActivity.java (versión 2)
package com.especialcursos.menus;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
public class EditorMusicaActivity extends Activity {
static int CD_VINILO = 0;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_musica, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Se gestiona la opción seleccionada
switch (item.getItemId()) {
case R.id.buscar:
buscar();
return true;
case R.id.escuchar:
escuchar();
return true;
case R.id.grabar:
grabar();
return true;
case R.id.cd_vinilo:
cd_vinilo();
return true;
// Las tres opciones del submenú de "Buscar" mostrarán el ListView
// (ResultadosMusicaActivity)
case R.id.grabaciones:
case R.id.internet:
case R.id.movil:
escuchar();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// Método invocado inmediatamente antes de mostrarse el menú, cada vez.
// Recibe como parámetro el Menu, con la estructura tal y como esté justo
// antes de ser mostrado.
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (CD_VINILO == 1) {
menu.findItem(R.id.cd_vinilo).setIcon(R.drawable.cd);
menu.findItem(R.id.cd_vinilo).setTitle(R.string.cd);
}
else {
menu.findItem(R.id.cd_vinilo).setIcon(R.drawable.vinilo);
menu.findItem(R.id.cd_vinilo).setTitle(R.string.vinilo);
}
// return false: no muestra menú
// return true: se muestra menú. Buena práctica: delegar en clase
// padre.
return super.onPrepareOptionsMenu(menu);
}
private void cd_vinilo() {
if (CD_VINILO == 0)
CD_VINILO = 1;
else
CD_VINILO = 0;
}
private void escuchar() {
// Se solicita al sistema iniciar la actividad
// ResultadosMusicaActivity
Intent intent = new Intent(EditorMusicaActivity.this,
ResultadosMusicaActivity.class);
startActivity(intent);
}
private void buscar() {
// TODO
}
private void grabar() {
// TODO
}
}
Código de ResultadosMusicaActivity.java
package com.especialcursos.menus;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
// Esta actividad no extiende de Activity, si no de una subclase suya,
// ListActivity, que crea automáticamente una lista de items a pantalla
// completa
public class ResultadosMusicaActivity extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// La lista de items se cargan en un array. Los elementos a
// cargar están en el archivo strings.xml
String[] resultadosMusica =
getResources().getStringArray(R.array.musica_array);
// Cada elemento de la lista será un TextView, definido en el
// layout resultado_musica.xml
setListAdapter(new ArrayAdapter<String>(this,
R.layout.resultado_musica, resultadosMusica));
// NOTA: se pueden utilizar layouts predefinidos como
// "android.R.layout.simple_list_item_1"
//setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, resultadosMusica));
// Se obtiene la vista tipo lista
ListView lv = getListView();
// Se activa su el flitrado de texto en la lista.
// Cuando el usuario comience a escribir, la lista se filtrará
// automáticamente
lv.setTextFilterEnabled(true);
// Se mostrará un pequeño mensaje flotante al usuario (Toast)
// cuando se haga click en un ítem
// Este método define el listener tipo onClick para cada
// elemento de la lista.
// Cuando se pulse un elemento de la lista, se invocará al
// método onItemClick y se mostrará la Toast
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int
position, long id) {
// Cuando se haga click, se mostrará un popup (toast o
// "tostada")
//con el texto del TextView
Toast.makeText(getApplicationContext(), ((TextView)
view).getText(), Toast.LENGTH_SHORT).show();
}
});
// Con este método se asigna el menú contextual al ListView
registerForContextMenu(lv);
}
// Método que creará el menú contextual para el ListView
// El menú contextual está definido en menu_lista_musica.xml
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo
menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_lista_musica, menu);
}
// Acciones que se realizarán al seleccionar un ítem del menú contextual
// Por el momento, ninguna
@Override
public boolean onContextItemSelected(MenuItem item) {
// El menuItem recibido es sobre el que se ha mantenido la pulsación
// para mostrar el menú contextual.
// "info" contiene el identificador del menuItem seleccionado, sobre
// el que se realizará la acción del menú contextual.
AdapterContextMenuInfo info = (AdapterContextMenuInfo)
item.getMenuInfo();
switch (item.getItemId()) {
case R.id.escuchar:
//escuchar(info.id);
return true;
case R.id.enviar:
//enviar(info.id);
return true;
case R.id.borrar:
//borrar(info.id);
return true;
default:
return super.onContextItemSelected(item);
}
}
}
Código de main.xml
<?xml version="1.0" encoding="utf‐8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
</LinearLayout>
Código de menu_lista_musica.xml (Menú Contextual)
<?xml version="1.0" encoding="utf‐8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/escuchar"
android:title="@string/escuchar">
</item>
<item
android:id="@+id/enviar"
android:title="@string/enviar">
</item>
<item
android:id="@+id/borrar"
android:title="@string/borrar">
</item>
</menu>
Código de resultado_musica.xml (ítem list de ListView)
<?xml version="1.0" encoding="utf‐8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="12dp"
android:textSize="18sp" >
</TextView>
Código de strings.xml (incluye textos para elemento de lista)
<?xml version="1.0" encoding="utf‐8"?>
<resources>
<string name="hello">Ejemplos Menús. Música y más música!</string>
<string name="app_name">MenusYActionBar</string>
<string name="buscar">Buscar</string>
<string name="grabar">Grabar</string>
<string name="escuchar">Escuchar</string>
<string name="vinilo">Vinilo</string>
<string name="enviar">Enviar</string>
<string name="borrar">Borrar</string>
<string name="cd">CD</string>
<string name="grabaciones">Grabaciones</string>
<string name="musica_movil">Música en Móvil</string>
<string name="musica_internet">Música en Internet</string>
<string‐array name="musica_array">
<item>Foo Fighters</item>
<item>Fun Lovin Criminals</item>
<item>Fundamental</item>
<item>Garbaje</item>
<item>Green Day</item>
<item>Heroes del Silencio</item>
<item>Hevia</item>
<item>Ibiza Chill Out</item>
<item>Indi Sound</item>
<item>Janis Joplins</item>
<item>Jarabe de Palo</item>
<item>Jazz Contemporaneo</item>
<item>Jazz ECM</item>
<item>Jimi Hendrix</item>
<item>Kula Shaker</item>
<item>Lemonheads</item>
<item>Mandragora</item>
<item>Manolo García</item>
<item>Manu Chao</item>
<item>Mark Knofler</item>
<item>Mike Oldfield</item>
<item>Mississippi</item>
<item>Nada Surf</item>
<item>Nirvana</item>
<item>No Doubt</item>
<item>Oasis</item>
<item>Ocean Colour Scene</item>
<item>Off Spring</item>
<item>Pixies</item>
<item>Plachi (Disco‐Bar)</item>
<item>Presidents Of The USA</item>
<item>Pulp </item>
<item>Radiohead</item>
<item>Ramones</item>
<item>Reef</item>
<item>REM</item>
<item>Revolver</item>
<item>Rodriguez</item>
<item>Safri Duo</item>
<item>Santana, Carlos</item>
<item>Scorpions</item>
<item>Seguridad Social</item>
<item>Sidone</item>
<item>Siniestro Total</item>
<item>Skunk Anansie</item>
</string‐array>
</resources>