En este post vamos a ver como se puede crear una barra lateral, o Navigation Drawer como la de la imagen para una aplicacion Android usando el widget DrawerLayout de la libreria support-v4

Instalando el soporte para DrawerLayout

Si queremos usar este widget lo primero que debemos hacer es instalar la liberia support-v4 siguiendo estos pasos:

  • Instalar la libreria desde el Android SDK Manager: Debemos tener instalada la opcion que pone Android Support Library
  • Hacer referencia a ella en nuestro proyecto: Si usamos Eclipse deberemos copiar la libreria que esta en $ANDROID_SDK_HOME//extras/android/support/v4 que se llama android-support-v4.jar a nuestro directorio libs del proyecto
    Si usamos Android Studio y usamos Gradle sera suficiente con poner una dependencia en nuestro archivo build.gradle

    compile 'com.android.support:support-v4:19.0.+'
    

Modificando el layout y la Activity del proyecto

Lo primero que tenemos que hacer es modificar el layout que vamos a usar en nuestra Activity de la siguiente manera

<android.support.v4.widget.DrawerLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/main_layout">

        <TextView
            android:id="@+id/message"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:choiceMode="singleChoice"
            android:text="@string/hello_world">

        </TextView>
    </RelativeLayout>

    <ListView
        android:id="@+id/navigation"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/white"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp" >
    </ListView>

</android.support.v4.widget.DrawerLayout>

Lo siguiente es crear nuestra Activity

package es.rubenjgarcia.emptydrawerlayout.app;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;


public class MainActivity extends Activity {

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

Una vez hecho esto tendremos lo siguiente en nuestro movil

drawerlayout-01

Si deslizamos el dedo desde la parte izquierda de nuestra pantalla hacia la derecha veremos como sale el menu quedando nuestra pantalla de la siguiente manera

drawerlayout-02

Añadiendo el icono del navegador

Lo siguiente que vamos a hacer es añadir un icono en la parte superior del menu a la izquierda de nuestro icono para advertir al usuario que tenemos un menu lateral. El icono que vamos a usar lo podemos encontrar en el Action Bar Icon Pack de Android. Usaremos el icono de las tres barras con la sombra. Lo podemos encontrar en la carpeta Navigation_Drawer_Indicator del pack que nos hemos bajado
Configuramos nuestro estilo de la aplicacion en el archivo AndroidManifest.xml para que sea Theme.Holo.Light.DarkActionBar

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="es.rubenjgarcia.emptydrawerlayout.app" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Holo.Light.DarkActionBar" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Una vez copiamos las carpetas que hay dentro del directorio Navigation_Drawer_Indicator/holo_light en nuestro proyecto mezclandolas con las que ya tenemos solo tenemos que añadir unas lineas a nuestra clase MainActivity para que salga el icono

package es.rubenjgarcia.emptydrawerlayout.app;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;


public class MainActivity extends Activity {

    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;

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

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);

        getActionBar().setDisplayHomeAsUpEnabled(true);
        getActionBar().setHomeButtonEnabled(true);

        final CharSequence mTitle = getTitle();
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {

            @Override
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getActionBar().setTitle(mTitle);
                invalidateOptionsMenu();
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActionBar().setTitle(mTitle);
                invalidateOptionsMenu();
            }
        };

        mDrawerLayout.setDrawerListener(mDrawerToggle);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        return mDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
    }
    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }
}

Las lineas importantes de este codigo son por un lado

getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);

Estas 2 lineas hacen que el icono muestre una imagen y que responda a los clicks

Por otro lado esta el metodo onOptionsItemSelected donde indicamos que si se ha hecho click en el icono del drawer queremos que sea nuestro objeto mDrawerToggle el que procese ese evento. Este objeto es el que define el icono que tiene y el comportamiento al abrir y cerrar la barra lateral

Con este codigo tendriamos las siguientes pantallas en nuestro telefono

drawerlayout-03 drawerlayout-04

Añadiendo opciones a la barra de navegacion

Lo unico que nos falta es añadir opciones a nuestra barra de navegacion. Vamos a crear un layout para cada uno de los elementos que apareceran en la barra

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@android:string/untitled"
        android:padding="10dp" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/icon"
        android:layout_centerVertical="true"
        android:paddingLeft="10dp"
        android:textColor="@android:color/black"
        android:textSize="18dp" />
</RelativeLayout>

Este layout contiene una imagen y un texto que es lo que vamos a renderizar en la lista. Lo siguiente es crear las clases de apoyo. La primera es un bean donde definiremos el texto de la opcion y la imagen que vamos a mostrar

package es.rubenjgarcia.emptydrawerlayout.app.navigation;

public class NavigationItem {
    private int imageId;
    private String title;

    public NavigationItem(int imageId, String title) {
        this.imageId = imageId;
        this.title = title;
    }

    public int getImageId() {
        return imageId;
    }

    public void setImageId(int imageId) {
        this.imageId = imageId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return title;
    }
}

Y la siguiente es nuestra implementacion de la clase ArrayAdapter donde compondremos la lista

package es.rubenjgarcia.emptydrawerlayout.app.navigation;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

import es.rubenjgarcia.emptydrawerlayout.app.R;

public class NavigationListAdapter extends ArrayAdapter<NavigationItem> {

    private Context context;

    public NavigationListAdapter(Context context, int resourceId, List<NavigationItem> items) {
        super(context, resourceId, items);
        this.context = context;
    }

    private class ViewHolder {
        ImageView imageView;
        TextView txtTitle;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        NavigationItem navigationItem = getItem(position);

        LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.navigation_item, null);
            holder = new ViewHolder();
            holder.txtTitle = (TextView) convertView.findViewById(R.id.title);
            holder.imageView = (ImageView) convertView.findViewById(R.id.icon);
            convertView.setTag(holder);
        } else
            holder = (ViewHolder) convertView.getTag();

        holder.txtTitle.setText(navigationItem.getTitle());
        holder.imageView.setImageResource(navigationItem.getImageId());

        return convertView;
    }
}

Vamos a añadir 3 opciones y los iconos los vamos a coger del pack de iconos que hemos descargado. En la carpeta Action_Bar_Icons/holo_light copiaremos los iconos de las carpetas 13_extra_actions_about, 13_extra_actions_help y 13_extra_actions_settings. Solo falta añadir el siguiente codigo a la clase MainActivity

List<NavigationItem> navigationItems = new ArrayList<NavigationItem>();
navigationItems.add(new NavigationItem(R.drawable.ic_action_settings, "Settings"));
navigationItems.add(new NavigationItem(R.drawable.ic_action_help, "Help"));
navigationItems.add(new NavigationItem(R.drawable.ic_action_about, "About"));

final ListView navigation = (ListView) findViewById(R.id.navigation);
NavigationListAdapter adapter = new NavigationListAdapter(this, R.layout.navigation_item, navigationItems);
navigation.setAdapter(adapter);

Deberiamos ver una pantalla similar a esta en nuestro telefono

drawerlayout-05

Podeis descargar el codigo de este proyecto para Android Studio en el siguiente repositorio de Github