Tema 7
Menús y ActionBar
TEMA 7. MENÚS Y ACTIONBAR
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 cualquier
aplicación.
Aunque es posible crear menús vía código en una actividad, no es esta una buena práctica
programática, 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 cada menú de la aplicación,
cuyo nombre será el identificador que utilizará la clase R para hacerlo accesible desde código.
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. Dicho elemento 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 ActionBar, tal y como se verá más adelante.
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 ActionBar (visible
desde la creación de la actividad).
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_musica, menu);
return true;
}
A partir de Android 3.0, existe la opción de asignar un evento tipo on-click 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 será invocado.
A partir de Android 3.0, como la ActionBar (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
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.
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, el cual será definido 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 sobre 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
@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) {
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);
}
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 objeto de tipo
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.
La vista ListView puede ser implementada fácilmente creando una actividad que herede de
ListActivity (subclase de Activity), que no necesitará asociar un layout (a través de
setContentView(id_layout)). Los ítems se insertan automáticamente en la lista gracias a un
ListAdapter, mediante el método setListAdapter(), que admite como único parámetro
un ArrayAdapter el cual gestionará el array de ítems que se dispondrán en la ListView. Los
parámetros que hay que pasar al constructor del ArrayAdapter son el contexto de la
aplicación (Context), un layout que define el aspecto de cada ítem y la lista de elementos que
se mostrarán en la ListView. ArrayAdapter construirá la pantalla con este layout que
define el aspecto de un único ítem de la lista, gracias a lo cual no es necesario establecer el
layout de la actividad en conjunto.
Nótese que, debido a que esta lista es un objeto tipo ListView, admite filtrados por texto.
Mantener pulsado
Pulsación sencilla
Toast
@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;
Código de ResultadosMusicaActivity.java
@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á una TextView, definida 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));
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_lista_musica, menu);
}
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
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
</LinearLayout>
Existen otras características que se pueden aplicar a la mayor parte de los ítems de menú:
Grupos
Para crear un grupo de ítems en el archivo XML bastará con anidar con la etiqueta <group> el
conjunto de elementos <item> deseado. En código, se utilizará un ID común al invocar al
método Menu.add() 2.
<item
android:id="@+id/escuchar"
android:title="@string/escuchar">
<menu>
<item android:id="@+id/reproductor_defecto"
android:title="@string/rep_0" />
<group android:id="@+id/reproductores">
<item android:id="@+id/reproductor1"
android:title="@string/rep_1" />
<item android:id="@+id/reproductor2"
android:title="@string/rep_2" />
…
</group>
</menu>
</item>
2
Tal y como ya se ha mencionado, salvo excepciones, no es una buena práctica añadir ítems de menú dentro del
código Java. Deberán ser definidos en el archivo XML siempre que sea posible.
En el ejemplo anterior, todos los ítems del menú (agrupados o no) son hermanos y tendrán el
mismo aspecto. Por otro lado, se podrá modificar exclusivamente el aspecto de los ítems del
grupo, referenciando al identificador del grupo, reproductores.
Ítems seleccionables
Exceptuando los menús de opciones (o de iconos), que se muestran pulsando el botón menú
(físico o soft) del dispositivo, cualquier otro ítem de menú puede tener asociada una checkbox
o un radiobutton. Esta característica es útil cuando se quiere utilizar un menú para mostrar
opciones cuyo estado pueda ser eleccionado por el usuario. En el caso de que todos los ítems
de una lista puedan ser seleccionados, se utilizarán checkboxes. En cambio, si se trata de una
lista de elementos mutuamente excluyentes, se utilizarán radiobuttons.
Para activar esta característica de forma individual se utilizará la propiedad del elemento
<item>, android:checkable="true"; para crear una lista de ítems mutuamente excluyentes,
estos deberán agruparse en un <group android:checkableBehaviour="single"> 3
<item
android:id="@+id/grabar"
android:title="@string/grabar"
android:icon="@drawable/mic">
<menu>
<item
android:id="@+id/calidad_alta"
android:title="@string/calidad_alta"
android:checkable="true"
android:checked="false" />
<group android:checkableBehavior="single">
<item
android:id="@+id/mp3"
android:title="@string/mp3" />
<item
android:id="@+id/aac"
android:title="@string/aac" />
<item
android:id="@+id/wma"
android:title="@string/wma" />
</group>
</menu>
</item>
3
Si android:checkableBehaviour="all", se creará un grupo de ítems con checkboxes asociados. Todos podrán
ser seleccionados.
El resultado es el siguiente:
case R.id.calidad_alta:
if (item.isChecked())
item.setChecked(false);
else
item.setChecked(true);
return true;
case R.id.mp3:
case R.id.aac:
case R.id.wma:
item.setChecked(true);
return true;
Con este código, se conseguirá mantener las opciones seleccionadas previamente. Por
ejemplo, si en el menú “Grabar” se pulsa en “Alta calidad” y, posteriormente en “AAC”, al
abrirlo por tercera vez, dichos ítems estarán activados:
Los ítems que se hayan seleccionado sólo serán recordados mientras la actividad no finalice,
por lo que, si se desea conservar su estado la próxima vez que se inicie la aplicación, deberán
almacenarse dichos datos a través de la clase SharedPreferences, lo cual se estudiará en un
próximo tema.
Atajos de teclado
Se pueden definir atajos de teclado para aquellos dispositivos con teclado físico, que
permitirán un acceso rápido a diferentes opciones del menú. Para implementar estos atajos, se
utilizarán las propiedades android:alphabeticShortcut o android:numericShortcut, en
el elemento <item>, o bien los métodos setAphabeticShortcut() o
setNumericShortcut(), que admiten un carácter como parámetro.
Debido a la tendencia evolutiva del sistema Android, que aboga por crear una interfaz con el
usuario completamente táctil, sin botones físicos en los dispositivos, los atajos de teclado
serán cada vez menos usados.
ActionBar
Gracias a este nuevo elemento, se puede dar acceso instantáneo a acciones importantes, al
colocar ítems del menú de opciones en la ActionBar, convirtiéndolos en action items los cuales,
a su vez, proverán action views, widgets que hacen accesibles comportamientos concretos de
forma inmediata.
Para colocar un ítem de menú en la ActionBar convirtiéndolo en un action item, será necesario
añadir a la etiqueta <item>, en el archivo XML que define el menú, el atributo
android:showAsAction="ifRoom" o bien android:showAsAction="ifRoom|withText" 4. En
el primer caso se mostrará solamente el icono del ítem (en caso de haber espacio suficiente en
la ActionBar), mientras que en el segundo caso, se mostrará tanto el icono como el texto
asociado al ítem (de nuevo, en función del espacio disponible, y prevaleciendo siempre el
icono). Por otro lado, el resto de ítems que no sean mostrados en la ActionBar serán accesibles
desde el menú desplegable derecho 5, y nunca mostrarán su icono asociado.
En el ejemplo utilizado en este tema, se han introducido los mínimos cambios necesarios para
mostrar la ActionBar. Para poder visualizar la aplicación, es necesario utilizar AVDs con Android
3.0 o superior.
4
Estos atributos solo están disponibles a partir de Android 3.0.
5
o inferior, en el caso de dispositivos móviles con la versión ICS (Android 4.0)
Código de main.xml
Código de menu_musica.xml
Código de AndroidManifest.xml
<application
android:icon="@drawable/app_icon"
android:label="@string/app_name" >
<activity
android:name=".EditorMusicaActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ResultadosMusicaActivity"></activity>
</application>
</manifest>
Una vez realizadas las modificaciones señaladas en negrita, será necesario crear nuevos AVDs
para poder visualizar el resultado.
En las imágenes siguientes se muestra la configuración de tres AVDs diferentes, así como el
aspecto del menú sobre dichas versiones.
AVD HoneyComb
Resultado:
AVD ICS
Resultado: