Sie sind auf Seite 1von 17

Interfaz de Usuario en Android:

Navigation Drawer (NavigationView)


En este nuevo tema vamos a tratar otro de los componentes relacionados con

la interfaz de nuestras aplicaciones: el menú lateral deslizante, o Navigation

Drawer. Este menú es el que aparece en muchas aplicaciones al deslizar el

dedo desde el borde izquierdo de la pantalla hacia el lado opuesto (también

puede aparecer en el lado derecho, pero es menos frecuente).

El navigation drawer está disponible como parte de la librería de

compatibilidad android-support-v4. Si estamos trabajando con Android Studio,

en la mayoría de los casos no tendremos que añadir esta librería al proyecto

de forma explícita ya que muy probablemente tengamos ya añadida la

librería appcompat-v7 (se incluye por defecto en los proyectos de Android

Studio), que depende de la primera.

En este tema vamos a crear un menú lateral que cumpla todo lo posible las

directrices marcadas por las nuevas guías de diseño de Material Design.

Dicha guía establece que el menú de navegación lateral debe pasar por

encima de la action bar y por debajo de la status bar (que será translúcida en

Android 5.0 y posteriores).


Veremos cómo conseguir este efecto para versiones de Android a partir de la

5.0 Lollipop (API 21), mientras que en versiones anteriores la status bar

simplemente permanecerá de color negro.

Vamos a empezar a crear nuestra aplicación, y comenzaremos como siempre

creando la interfaz de usuario. Para añadir el navigation drawer a una

actividad debemos hacer que el elemento raíz del layout XML sea del tipo

<android.support.v4.widget.DrawerLayout>. Y dentro de este

elemento colocaremos únicamente 2 componentes principales (en el orden

indicado):

• El layout real de la actividad, que en casos como este suele pasar a

llamarse ‘content.layout’.

• El layout del menú lateral, que entre otras cosas hará las veces de

contenedor de las distintas opciones del menú lateral.

El primero de estos elementos lo añadiremos por ahora en forma

de <include> y después volveremos sobre él. Para el segundo vamos a

utilizar otro de los nuevos componentes incluidos con la nueva librería de


diseño de Android (Design Support Library). El componente en cuestión es el

llamado NavigationView, que nos ayudará bastante en la construcción del

layout del menú lateral.

En nuestro caso de ejemplo quedaría como sigue

(/res/layout/activity_main.xml):

1 <android.support.v4.widget.DrawerLayout
2 xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 android:id="@+id/drawer_layout"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:fitsSystemWindows="true"
9 tools:context=".MainActivity">
10
11 <!-- Layout real de la actividad -->
12 <include layout="@layout/content_layout" />
13
14 <!-- Layout del menú lateral (Navigation View) -->
15 <android.support.design.widget.NavigationView
16 android:id="@+id/navview"
17 android:layout_width="wrap_content"
18 android:layout_height="match_parent"
19 android:fitsSystemWindows="true"
20 android:layout_gravity="start"
21 app:headerLayout="@layout/header_navview"
22 app:menu="@menu/menu_navview" />
23
24 </android.support.v4.widget.DrawerLayout>
Varios detalles a destacar en el código anterior. En cuanto

al DrawerLayout es importante asignar a true la propiedad

android:fitsSystemWindows, que ayudará a conseguir el efecto indicado

de deslizamiento del menú por debajo de la status bar.

Respecto al NavigationView, vemos como también asignamos su

propiedad fitsSystemWindows de forma análoga al DrawerLayout.


Posteriormente asignamos las propiedades quizá más relevantes del nuevo

componente:

• La primera de ellas, android:layout_gravity, determina el lado de

la pantalla por el que aparecerá el menú deslizante (“start” para que

aparezca por la izquierda, o “end” por la derecha).

• Con app:headerLayout (opcional) asignamos al menú lateral el layout

XML de su cabecera, es decir, de la zona que queda por encima de la

lista de opciones del menú.

• Por último, con app:menu, indicamos el recurso de menú que

mostraremos en el navigation drawer. El componente

NavigationView utiliza el sistema de menús habitual de Android, por

lo que este menú podemos definirlo de forma análoga a como ya lo

hicimos por ejemplo en el tema dedicado a la action bar para definir el

menú de overflow, aunque más adelante mostraremos alguna

peculiaridad.

Veamos a continuación la definición del layout XML utilizado como cabecera

del navigation drawer (/res/layout/header_navview.xml). En la

cabecera del menú lateral suele incluirse en muchas aplicaciones información

sobre el usuario logueado, y opciones para cambiar de usuario si esto fuera

posible. En este caso, para no complicar el ejemplo, vamos a utilizar tan sólo
una imagen de fondo y una etiqueta de texto, aunque aclarar que este layout

puede ser todo lo complejo que sea necesario.

1
2
3
4
5
6
7 <FrameLayout
8 xmlns:android="http://schemas.android.com/apk/res/android"
9 android:layout_width="match_parent"
android:layout_height="match_parent">
1
0
<ImageView
1
android:layout_width="match_parent"
1
android:layout_height="200dp"
1
android:src="@drawable/navheader"
2
android:scaleType="centerCrop" />
1
3
<TextView
1
android:layout_width="wrap_content"
4
android:layout_height="wrap_content"
1 android:text="@string/usuario"
5 android:textAppearance="@style/TextAppearance.AppCompat.Large.Invers
1 e"
6 android:textStyle="bold"
1 android:layout_gravity="bottom"
7 android:layout_marginBottom="10dp"
1 android:layout_marginLeft="10dp" />
8 </FrameLayout>
1
9
2
0
2
1
Respecto al menú a utilizar, ya dijimos que se definirá utilizando la sintaxis

habitual de los recursos de tipo menú. En mi caso añadiré tres

secciones principales y dos opciones adicionales:

1 <menu xmlns:android="http://schemas.android.com/apk/res/android">
2 <group android:checkableBehavior="single">
3 <item
4 android:id="@+id/menu_seccion_1"
5 android:icon="@drawable/ic_menu"
6 android:title="@string/seccion_1"/>
7 <item
8 android:id="@+id/menu_seccion_2"
9 android:icon="@drawable/ic_menu"
10 android:title="@string/seccion_2"/>
11 <item
12 android:id="@+id/menu_seccion_3"
13 android:icon="@drawable/ic_menu"
14 android:title="@string/seccion_3"/>
15 </group>
16
17 <item
18 android:id="@+id/navigation_subheader"
19 android:title="@string/otras_opciones">
20 <menu>
21 <item
22 android:id="@+id/menu_opcion_1"
23 android:icon="@drawable/ic_menu"
24 android:title="@string/opcion_1"/>
25 <item
26 android:id="@+id/menu_opcion_2"
27 android:icon="@drawable/ic_menu"
28 android:title="@string/opcion_2"/>
29 </menu>
30 </item>
31 </menu>
Comentemos algunos detalles de la definición anterior. En primer lugar

vemos que las tres secciones principales se engloban en un

elemento <group> al que hemos asignado su propiedad

checkableBehavior con valor “single“. Con esto indicamos que sólo

pueda seleccionarse una de estas tres opciones al mismo tiempo (más tarde

veremos como resaltar la opción seleccionada). A continuación se

añaden dos opciones más dentro de un submenú al que asignamos su título

con la propiedad android:title. Este título del submenú aparecerá en

forma de cabecera de sección dentro del menú, incluyendo incluso una línea

de división tras las tres opciones anteriores. Adicionalmente, en todas las

opciones de menú indicamos su id (android:id), su icono

(android:icon) y su título (android:title). En nuestro caso se ha


utilizado por simplicidad el mismo icono en todas las opciones, pero por

supuesto pueden ser distintos.

En una imagen veremos mejor el resultado:


Como nota importante indicar que las opciones incluidas dentro de un

submenú no es posible resaltarlas en la interfaz como sí ocurre con las

opciones anteriores incluidas dentro del elemento group. Es posible que en

futuras versiones de la librería de diseño se habilite esta posibilidad.

Veamos a continuación el layout de la actividad principal. Para este ejemplo

utilizaré un layout muy similar a los ya mostrados en los temas sobre la action

bar. Contendrá tan sólo un Toolbar, que estableceremos como action bar en

el onCreate() de la actividad, y un FrameLayout que nos servirá como

contenedor de los fragment que contendrán cada sección del menú lateral.

Dicho de otra forma, cada sección principal de la aplicación la

implementaremos mediante un fragment independiente, y al pulsar cada

opción del menú lateral, instanciaremos el fragment de su tipo

correspondiente y lo colocaremos en el lugar del FrameLayout indicado.

Veamos cómo quedaría (/res/layout/content_layout.xml):

1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="vertical"
6 android:clickable="true"
7 tools:context=".MainActivity"
8 tools:showIn="@layout/activity_main">
9
10 <!-- Toolbar -->
11 <android.support.v7.widget.Toolbar
12 xmlns:app="http://schemas.android.com/apk/res-auto"
13 android:id="@+id/appbar"
14 android:layout_height="?attr/actionBarSize"
15 android:layout_width="match_parent"
16 android:minHeight="?attr/actionBarSize"
17 android:background="?attr/colorPrimary"
18 android:elevation="4dp"
19 android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
20 app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
21
22 <!-- Resto de la interfaz de usuario -->
23 <FrameLayout
24 android:id="@+id/content_frame"
25 android:layout_width="match_parent"
26 android:layout_height="match_parent" />
27
28 </LinearLayout>
Para que la toolbar se visualice correctamente habrá que definir por supuesto

el tema de la aplicación, los colores principales, … tal y como ya vimos en el

tema sobre el componente Toolbar.

Un último detalle de la configuración XML de la aplicación, y que nos servirá

para terminar de conseguir el efecto deseado. Para versiones de Android 5.0

o superior (API >= 21) debemos añadir a la definición del tema algunos

atributos adicionales, para lo que crearemos un

fichero styles.xml específico de dicha versión (/res/values-

21/styles.xml) con las siguientes definiciones:

<resources>
1
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
2
<item name="colorPrimary">@color/colorPrimary</item>
3
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
4
<item name="colorAccent">@color/colorAccent</item>
5
6
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
7
<item
8 name="android:statusBarColor">@android:color/transparent</item>
9 </style>
10 </resources>
Definida la interfaz XML, nos centramos ya en la parte java de la actividad. En

primer lugar vamos a crear los fragments que mostraremos al seleccionar

cada una de las tres opciones del menú de navegación. En este paso no nos
vamos a complicar ya que no es el objetivo de este artículo. Voy a crear un

fragment por cada opción, que contenga tan sólo una etiqueta de texto

indicando la opción a la que pertenece. Obviamente en la práctica esto no

será tan simple y habrá que definir cada fragment para que se ajuste a las

necesidades de la aplicación.

Como ejemplo muestro el layout XML y la implementación java de uno de los

layout. Primero el layout (fragment_fragment1.xml) :

<FrameLayout
1
xmlns:android="http://schemas.android.com/apk/res/android"
2
xmlns:tools="http://schemas.android.com/tools"
3
android:layout_width="match_parent"
4
android:layout_height="match_parent"
5
tools:context="dam.miguel.android.a23navigationdrawer.Fragment1">
6
7
<TextView
8
android:layout_width="match_parent"
9 android:layout_height="match_parent"
10 android:text="@string/fragment1" />
11
12 </FrameLayout>

Y su clase java asociada (Fragment1.java), que se limitará a inflar el

layout anterior:

1 import android.os.Bundle;
2 import android.support.v4.app.Fragment;
3 import android.view.LayoutInflater;
4 import android.view.View;
5 import android.view.ViewGroup;
6
7 public class Fragment1 extends Fragment {
8
9 public Fragment1() {
10 // Required empty public constructor
11 }
12
13 @Override
14 public View onCreateView(LayoutInflater inflater, ViewGroup container,
15 Bundle savedInstanceState) {
16 // Inflate the layout for this fragment
17 return inflater.inflate(R.layout.fragment_fragment1, container,
18 false);
19 }
}
Los dos fragments restantes serán completamente análogos al mostrado.

Lo siguiente será implementar la lógica necesaria para responder a los

eventos del menú de forma que cambiemos de fragment al pulsar cada

opción. Esto lo haremos implementando el evento

onNavigationItemSelected()del control NavigationView del menú

lateral, lógica que añadiremos al final del método onCreate() de nuestra

actividad principal.

1 drawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
2 navView = (NavigationView)findViewById(R.id.navview);
3
4 navView.setNavigationItemSelectedListener(
5 new NavigationView.OnNavigationItemSelectedListener() {
6 @Override
7 public boolean onNavigationItemSelected(MenuItem menuItem) {
8
9 boolean fragmentTransaction = false;
10 Fragment fragment = null;
11
12 switch (menuItem.getItemId()) {
13 case R.id.menu_seccion_1:
14 fragment = new Fragment1();
15 fragmentTransaction = true;
16 break;
17 case R.id.menu_seccion_2:
18 fragment = new Fragment2();
19 fragmentTransaction = true;
20 break;
21 case R.id.menu_seccion_3:
22 fragment = new Fragment3();
23 fragmentTransaction = true;
24 break;
25 case R.id.menu_opcion_1:
26 Log.i("NavigationView", "Pulsada opción 1");
27 break;
28 case R.id.menu_opcion_2:
29 Log.i("NavigationView", "Pulsada opción 2");
30 break;
31 }
32
33 if(fragmentTransaction) {
34 getSupportFragmentManager().beginTransaction()
35 .replace(R.id.content_frame, fragment)
36 .commit();
37
38 menuItem.setChecked(true);
39 getSupportActionBar().setTitle(menuItem.getTitle());
40 }
41
42 drawerLayout.closeDrawers();
43
44 return true;
45 }
46 });
Comentemos un poco el código anterior.

Para las tres secciones principales lo que hacemos en primer lugar es crear

el nuevo fragment a mostrar dependiendo de la opción pulsada en el menú de

navegación, que nos llega como parámetro (menuItem) del evento

onNavigationItemSelected. En el siguiente paso hacemos uso

del Fragment Manager con getSupportFragmentManager() para sustituir

el contenido del FrameLayout que definimos en el layout de la actividad

principal por el nuevo fragment creado. Posteriormente marcamos como

seleccionada la opción pulsada del menú mediante el

método setChecked() y actualizamos el título de la action bar por el de la

opción seleccionada mediante setTitle().

Por su parte, para las dos opciones finales del menú podemos realizar por

ejemplo cualquier otra acción que no implique cambio de fragment (como

abrir una actividad independiente para mostrar una ayuda o las opciones de
la aplicación). En este caso de ejemplo nos limitamos a mostrar un mensaje

de log llamando a Log.i().

Por último, y en cualquier caso, cerramos el menú llamando al

método closeDrawers() del DrawerLayout.

Bien, pues ya tenemos la funcionalidad básica implementada, y sólo nos

quedaría ajustar algunos detalles más para finalizar el trabajo. Los más

importantes: deberíamos mostrar un indicador en la action bar que evidencie

al usuario la existencia del menú lateral y deberíamos además permitir al

usuario abrirlo haciendo click en dicho icono (además del gesto de deslizar

desde el borde izquierdo hacia la derecha). Hasta la llegada de la nueva

librería de diseño, esto se realizaba haciendo uso del

componente ActionBarDrawerToggle, pero con la nueva librería el

proceso se ha simplificado bastante y ya no es necesario la utilización de

dicha clase.

Debemos primero incluir en nuestra aplicación el icono habitual que indica la

existencia de un navigation drawer (hamburguer icon). Puedes crear tu propio

icono desde cero, generarlo y descargarlo utilizando alguna herramienta

online como Android Asset Studio, o simplemente descargar el que se ha

utilizado como ejemplo para este artículo (se llama ic_nav_menu, y debes
descargar el icono en todas sus resoluciones, desde las carpetas de

drawables mdpi, hdpi, xhdpi, xxhdpi y xxxhdpi).

Una vez tenemos el icono incluido al proyecto, podemos utilizarlo como

indicador del menú lateral llamando al método

setHomeAsUpIndicator() desde el onCreate() de la actividad principal.

Adicionalmente habilitaremos esta funcionalidad llamando también

a setDisplayHomeAsUpEnabled().

1 @Override
2 protected void onCreate(Bundle savedInstanceState) {
3
4 //...
5
6 appbar = (Toolbar)findViewById(R.id.appbar);
7 setSupportActionBar(appbar);
8
9 getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_nav_menu);
10 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
11
12 //...
13 }
Adicionalmente, tendremos que capturar en el método

onOptionsItemSelected() de la actividad principal si se ha pulsado el

icono. Para ello, evaluaremos si la opción de menú pulsada

es android.R.id.home, en cuyo caso abriremos el menú lateral llamando

al método openDrawer() del DrawerLayout:

1 @Override
2 public boolean onOptionsItemSelected(MenuItem item) {
3
4 switch(item.getItemId()) {
5 case android.R.id.home:
6 drawerLayout.openDrawer(GravityCompat.START);
7 return true;
8 //...
9 }
10
11 return super.onOptionsItemSelected(item);
12 }
Llegados aquí, podemos ejecutar el proyecto y ver si todo funciona

correctamente. Verificaremos que el menú se abra, que contiene las opciones

indicadas y que al pulsar sobre ellas aparece en pantalla el contenido

asociado. Verificaremos además que el título de la action bar se va

actualizando según el estado del menú y la opción seleccionada.

Si lo ejecutamos sobre Android 5.x lo veremos como se muestra en la imagen

anterior de este artículo (el menú se desliza por debajo de la status bar

translúcida), y sobre Android 4.x se vería de forma casi idéntica a excepción

de la barra de estado que sería negra:


Por último del todo. El DrawerLayout dispone de una serie de eventos que

podemos cazar y actuar en consecuencia si así lo necesitáramos:

//Eventos del Drawer Layout


drawerLayout.setDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
Cuando deslizamos con el dedo
}

@Override
public void onDrawerOpened(View drawerView) {
Cuando se abre el drawer
}

@Override
public void onDrawerClosed(View drawerView) {
Cuando se cierra el drawer
}
@Override
public void onDrawerStateChanged(int newState) {
Con cualquier cambio de estado
}});

Das könnte Ihnen auch gefallen