Android >> Ejemplo de Navigation Drawer en Android ( Parte 2 )

navigation-drawer-parte-2

En la primera parte de este tutorial vimos como añadir un elemento Navigation Drawer en nuestra aplicación y listar en él una serie de elementos. En esta segunda parte vamos a ver como navegar por una aplicación usando el Navigation Drawer, implementar alguna funcionalidad cuando se está abriendo o cerrando, o utilizar el botón de la aplicación mostrado en la Action Bar para abrirlo y cerrarlo.

Añadiendo ActionBarCompat a nuestra aplicación

Vamos a empezar añadiendo un ActionBarCompat a nuestra aplicación, ya que será necesario para algunos de los puntos que vamos a tratar en este tutorial. Los pasos a seguir para añadir ActionBarCompat a nuestra aplicación son los siguientes:

  1. Añadimos a nuestro espacio de trabajo (workspace) el proyecto android-support-v7-appcompat pinchando en File → Import → Android → Existing Android Code Into Workspace.
  2. Seleccionamos dentro del directorio donde tenemos instalado el SDK de Android el directorio sdk/extras/android/support/v7/appcompat y pulsamos en Finish.
  3. Se añadirá a nuestro workspace el proyecto android-support-v7-appcompat, seleccionamos dentro de él el directorio lib y dentro de este hay dos librerías: android-support-v4.jar y android-support-v7-appcompat.jar, hacemos clic derecho sobre cada una de ellas y seleccionamos Build Path → Add to Build Path.
  4. A continuación hacemos clic derecho sobre el proyecto y seleccionamos Build Path → Configure Build Path. Ahora en la sección Java Build Path seleccionamos la pestaña Order and Export, donde seleccionamos las dos librerias que hemos añadido en el paso anterior, desmarcamos Android Dependencies y si aparece desmarcada la librería Android X.Y, la marcamos.
  5. El siguiente paso es hacer clic derecho sobre nuestra aplicación y seleccionamos Build Path → Configure Build Path. Seleccionamos la sección Android y dentro de esta nos vamos a Library donde pinchamos en el botón Add y seleccionamos android-support-v7-appcompat.jar. Pulsamos Apply y OK.
  6. Ahora, en nuestra aplicación, abrimos el fichero AndroidManifest.xml y en las actividades que queremos mostrar el action bar añadimos o modificamos el atributo android:theme dentro de la etiqueta <activity>:
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:theme="@style/Theme.AppCompat.Light.DarkActionBar" >
    

    También podremos indicar que queremos mostrar el action bar en toda la aplicación definiendo este atributo en la etiqueta <application>:

        <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/Theme.AppCompat.Light.DarkActionBar" >
    

    Ahora, cada actividad en la que queremos mostrar el action bar deberá heredar de la clase ActionBarActivity:

    import android.support.v7.app.ActionBarActivity;
    
    public class MainActivity extends ActionBarActivity {
    
    }
    

El uso principal de un navigation drawer es el de servir como índice de las actividades o funciones que va a tener nuestra aplicación. En este ejemplo vamos a crear un fragment al que se le pasará un texto dependiendo de la opción seleccionada en nuestro drawer:

res / layout / fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_fragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

MyFragment.java

package com.amatellanes.android.examples;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class MyFragment extends Fragment {

	public final static String KEY_TEXT = "key_text";

	private String mText;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		mText = getArguments().getString(KEY_TEXT);

		View v = inflater.inflate(R.layout.fragment, container, false);
		TextView tv = (TextView) v.findViewById(R.id.tv_fragment);
		tv.setText(mText);

		return v;
	}
}

Cuando el usuario selecciona un elemento de la lista que se muestra en el drawer, el sistema invoca al método onItemClick() definido en el interfaz OnItemClickListener. En este ejemplo modificaremos el contenido del fragment y el título mostrado en el action bar dependiendo del elemento seleccionado en el drawer :

MainActivity.java

package com.amatellanes.android.examples;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends ActionBarActivity {

	private DrawerLayout drawerLayout;
	private ListView navList;
	private CharSequence mTitle;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		this.drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
		this.navList = (ListView) findViewById(R.id.left_drawer);

		// Load an array of options names
		final String[] names = getResources().getStringArray(
				R.array.nav_options);

		// Set previous array as adapter of the list
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_list_item_1, names);
		navList.setAdapter(adapter);
		navList.setOnItemClickListener(new DrawerItemClickListener());
	}

	private class DrawerItemClickListener implements
			ListView.OnItemClickListener {
		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position,
				long id) {
			selectItem(position);
		}
	}

	/** Swaps fragments in the main content view */
	private void selectItem(int position) {
		// Get text from resources
		mTitle = getResources().getStringArray(R.array.nav_options)[position];

		// Create a new fragment and specify the option to show based on
		// position
		Fragment fragment = new MyFragment();
		Bundle args = new Bundle();
		args.putString(MyFragment.KEY_TEXT, mTitle.toString());
		fragment.setArguments(args);

		// Insert the fragment by replacing any existing fragment
		FragmentManager fragmentManager = getSupportFragmentManager();
		fragmentManager.beginTransaction()
				.replace(R.id.content_frame, fragment).commit();

		// Highlight the selected item, update the title, and close the drawer
		navList.setItemChecked(position, true);
		getSupportActionBar().setTitle(mTitle);
		drawerLayout.closeDrawer(navList);
	}

}

Como vemos en la línea 36 usamos el método setOnItemClickListener() para definir el evento que se debe lanzar, al igual que hacemos en cualquier otra vista. Se invocará al método selectItem() donde se realizan 4 pasos:

  • Obtener a partir de la posición del item pulsado el string que se va a mostrar en el fragment.
  • Crear el fragment que se va a mostrar pasando como argumento el texto obtenido en el paso anterior.
  • Mostrar el fragment en pantalla reemplazando el que ya está por pantalla.
  • Marcar en el drawer el elemento que hemos seleccionado, con el método setTitle() actualizar el título en el action bar y cerrar el drawer.

Eventos al abrir y cerrar el Navigation Drawer

En algunos casos necesitaremos implementar alguna funcionalidad al abrir o cerrar el drawer, para ello debemos usar el método setDrawerListener() de nuestro DrawerLayout y pasarle una implementacion de DrawerLayout.DrawerListener, que es el interfaz que nos proporcionará los métodos onDrawerOpened() y onDrawerClosed().

Sin embargo, si en tu aplicacion has incluido un action bar (como es el caso de este tutorial) se recomienda extender la clase ActionBarDrawerToggle, ya que, además de implementar el interfaz DrawerLayout.DrawerListener, facilita la correcta interacción entre la action bar y el drawer.

Vamos a ver a continuación como modificar el título y la visibilidad de los elementos del action bar dependiendo del estado del drawer (abierto o cerrado). Para ello vamos a comenzar insertando un menú de elementos en el action bar.

A continuación, definimos dos strings necesarios para crear un objeto ActionBarDrawerToggle, los cuales son necesario por temas de accesibilidad de la aplicación:

res / values / strings.xml

    <string name="open_drawer">Drawer abierto</string>
    <string name="close_drawer">Drawer cerrado</string>

Ahora vamos a definir el menú que aparecerá en nuestro action bar. Creamos el fichero res/menu/main.xml y definimos el número de elementos que vamos a añadir. Para que los elementos que añadamos aparezcan directamente como un botón de acción debemos incluir en cada etiqueta <item> el atributo showAsAction="ifRoom":

res / menu / main.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:yourapp="http://schemas.android.com/apk/res-auto" >

    <item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_action_search"
        android:title="Buscar"
        yourapp:showAsAction="ifRoom"/>

</menu>

Os recomiendo descargar los iconos oficiales de Android para la action bar que nos proporciona Google desde este enlace y que son los iconos que he usado en este tutorial.

Ahora en el código de nuestra actividad sobrescribimos el método onCreateOptionsMenu() e inflamos el menú que acabamos de crear para añadirlo en la action bar:

MainActivity.java

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

Para definir un ActionBarDrawerToggle indicamos en el constructor de la clase el elemento DrawerLayout que contiene drawer, un icono (puedes descargar el paquete de icono oficiales en este enlace), un recurso del tipo string para describir la apertura del drawer y otra para el cierre que se definen por temas de accesibilidad de la aplicación:

MainActivity.java

import android.support.v4.app.ActionBarDrawerToggle;
...

public class MainActivity extends ActionBarActivity {

	...

	private ActionBarDrawerToggle drawerToggle;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		mTitle = getTitle(); // Get current title

		this.drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
		this.navList = (ListView) findViewById(R.id.left_drawer);

		// Load an array of options names
		final String[] names = getResources().getStringArray(
				R.array.nav_options);

		// Set previous array as adapter of the list
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_list_item_1, names);
		navList.setAdapter(adapter);
		navList.setOnItemClickListener(new DrawerItemClickListener());
		drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
				R.drawable.ic_navigation_drawer, R.string.open_drawer,
				R.string.close_drawer) {

			/**
			 * Called when a drawer has settled in a completely closed state.
			 */
			public void onDrawerClosed(View view) {
				getSupportActionBar().setTitle(mTitle);
				// creates call to onPrepareOptionsMenu()
				supportInvalidateOptionsMenu();
			}

			/**
			 * Called when a drawer has settled in a completely open state.
			 */
			public void onDrawerOpened(View drawerView) {
				getSupportActionBar().setTitle("Selecciona opción");
				// creates call to onPrepareOptionsMenu()
				supportInvalidateOptionsMenu();
			}
		};

		// Set the drawer toggle as the DrawerListener
		drawerLayout.setDrawerListener(drawerToggle);

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	/*
	 * Called whenever we call invalidateOptionsMenu()
	 */
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		// If the nav drawer is open, hide action items related to the content
		// view
		boolean drawerOpen = drawerLayout.isDrawerOpen(navList);
		menu.findItem(R.id.action_search).setVisible(!drawerOpen);
		return super.onPrepareOptionsMenu(menu);
	}
}

Como vemos, en los métodos onDrawerOpened() y onDrawerClosed() se modifica el título del <em action bar, y para actualizar la vista del action bar llamamos al método supportInvalidateOptionsMenu() que invocará a su vez al método onPrepareOptionsMenu() que sobrescribimos.

Usando el icono de aplicación

Hasta ahora, abrimos y cerramos el drawer con un gesto de nuestro dedo, pero si estamos usando un action bar en nuestra aplicación, se recomienda habilitar la apertura y el cierre del drawer tocando el icono de la aplicación en la action bar. Además este icono debería indicar la existencia de un navigation drawer en la aplicación mostrando un icono adicional que podrás descargarte en este enlace. Para implementar este comportamiento deberemos hacer uso nuevamente de la clase ActionBarDrawerToggle:

MainActivity.java

import android.content.res.Configuration;
import android.view.MenuItem;
...

public class MainActivity extends ActionBarActivity {

	private DrawerLayout drawerLayout;
	private ActionBarDrawerToggle drawerToggle;
	private ListView navList;
	private CharSequence mTitle;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		...
		navList.setOnItemClickListener(new DrawerItemClickListener());
		drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
				R.drawable.ic_navigation_drawer, R.string.open_drawer,
				R.string.close_drawer) {

			/**
			 * Called when a drawer has settled in a completely closed state.
			 */
			public void onDrawerClosed(View view) {
				...
			}

			/**
			 * Called when a drawer has settled in a completely open state.
			 */
			public void onDrawerOpened(View drawerView) {
				...
			}
		};

		// Set the drawer toggle as the DrawerListener
		drawerLayout.setDrawerListener(drawerToggle);

		getSupportActionBar().setDisplayHomeAsUpEnabled(true);
		getSupportActionBar().setHomeButtonEnabled(true);
	}

	@Override
	protected void onPostCreate(Bundle savedInstanceState) {
		super.onPostCreate(savedInstanceState);
		// Sync the toggle state after onRestoreInstanceState has occurred.
		drawerToggle.syncState();
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		// Called by the system when the device configuration changes while your
		// activity is running
		super.onConfigurationChanged(newConfig);
		drawerToggle.onConfigurationChanged(newConfig);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Pass the event to ActionBarDrawerToggle, if it returns
		// true, then it has handled the app icon touch event
		if (drawerToggle.onOptionsItemSelected(item)) {
			return true;
		}
		// Handle your other action bar items...
		return super.onOptionsItemSelected(item);
	}
}

Usamos los métodos setDisplayHomeAsUpEnabled() y setHomeButtonEnabled() para habilitar la navegación a través del icono de la aplicación en la action bar y además debemos implementar los métodos onPostCreate(), onConfigurationChanged() y onOptionsItemSelected().

Descargar código | GitHub

Más información | Ejemplo de Navigation Drawer en Android (Parte 1), Creating a Navigation Drawer | Android Developers y Navigation Drawer | Android Developers