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:
-
Añadimos a nuestro espacio de trabajo (workspace) el proyecto
android-support-v7-appcompat
pinchando en File → Import → Android → Existing Android Code Into Workspace. - Seleccionamos dentro del directorio donde tenemos instalado el SDK de Android el directorio sdk/extras/android/support/v7/appcompat y pulsamos en Finish.
-
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
yandroid-support-v7-appcompat.jar
, hacemos clic derecho sobre cada una de ellas y seleccionamos Build Path → Add to Build Path. - 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.
-
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. - 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 { }
Navegando a través del Navigation Drawer
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()
.