Toolbar En Android: Creación De Action Bar En Material Design

De seguro ya sabes que la Action Bar como la conocíamos, está siendo reemplazada por un nuevo componente Android llamado Toolbar.

Las nuevas guías de diseño de Google han introducido el concepto de App Bar.

Una barra que tiene más flexibilidad de transformación y personalización que hace parte del checklist de diseño de Material Design:

Material Design: Uso De La Toolbar En Checklist De Diseño

Así que si deseas crear aplicaciones para Android 5 en adelante es necesario que aprendas a usarla.

Para ello he preparado este tutorial donde crearás una aplicación Android basada en una Toolbar.

Adicionalmente verás cómo extender su comportamiento  con los nuevos elementos de la librería de soporte para diseño, como lo son el CoordinatorLayout, AppBarLayout y CollapsingToolbarLayout.

Descargar Proyecto Android Studio De “Citas Peligrosas”

Si deseas ver el resultado final que obtendrás al final de seguir los pasos de este tutorial, entonces observa el siguiente video:

Para desbloquear el link de descargar del código completo, sigue estas instrucciones:

¿Cómo Añadir La Toolbar En Android?

Debido a que la Toolbar reemplaza a la antigua Action Bar, debes deshabilitarla con el estilo Theme.AppCompat.NoActionBar ó añadiendo los atributos windowActionBar y windowNoTitle con los siguientes valores:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Habilitar la Toolbar -->
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>

    <!--...-->
</style>

Luego de eso implementarla es muy fácil. Solo debes añadir una etiqueta <android.support.v7.widget.Toolbar>  en tus layouts como se hace con los widgets normales.

Android: Ejemplo De Toolbar Como Action Bar

Esta convención otorga todo el poder de la action bar al desarrollar Android, ya que el método antiguo solo permitía la personalización a través de los temas y estilos.

<!-- Toolbar -->
<android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

Algunos puntos a considerar:

Material Design: Toolbar Flexible Más Toolbar En CardView

  • Los action buttons de la Toolbar se inflan a través de un archivo de menú como siempre has hecho.
  • Los eventos se controlan igualmente con el método onOptionsItemSelected().
  • Debes encontrar la instancia de la Toolbar con findViewById() y luego setearla a la actividad con setSupportActionBar() para ponerla en funcionamiento.

CoordinatorLayout: Crear Interacciones Entre Views

La librería de soporte para el diseño de Android nos entrega el CoordinatorLayout para gestionar interaccione entre sus elementos hijos.

¿Qué tipos de interacciones?

Movimiento, animación y scrolling. Sobre todo nos da las herramientas para replicar las técnicas de scrolling en Material Design.

En este tutorial lo usaremos para crear interacciones entre la App Bar y views con scrolling. Sin embargo pueden usarse para otras relaciones.

AppBarLayout: Aumentando Las Capacidades De La Action Bar

Como se dijo al inicio, la App Bar es una expansión del concepto de la action bar. Aunque la Toolbar representa el pequeño segmento rectangular al que estamos acostumbrados, la App Bar (representada con la clase AppBarLayout) es un contenedor con más posibilidades de personalización.

Esta permite dividir los comportamientos de la action bar para independizar las acciones.

Incluso permite añadir efectos de scrolling en conjunto con el Coordinator Layout.

Por ejemplo…Ejemplo De Standard Toolbar En Android Con Scrolling

Si deseas esconder la Toolbar cuando el usuario desplaza un RecyclerView, debes envolver ambos elementos con un coordinator como se ve en el código de abajo.

<android.support.design.widget.CoordinatorLayout 
    ...>

    <!--Objeto Con Scroll -->
    <android.support.v7.widget.RecyclerView
        ...
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <!-- App Bar -->
    <android.support.design.widget.AppBarLayout
        ...>

        <!-- Toolbar -->
        <android.support.v7.widget.Toolbar 
            ...
            app:layout_scrollFlags="scroll|enterAlways" />

    </android.support.design.widget.AppBarLayout>


</android.support.design.widget.CoordinatorLayout>

Para ello debes tener en cuenta lo siguiente:

  • AppBarLayout siempre debe envolver a la Toolbar y los demás componentes pertenecientes a ella (como pestañas, imágenes, etc).
  • El CoordinatorLayout debe envolver directamente a los objetos que desees relacionar mediante una acción.
  • Desaparecer la App Bar por scrolling requiere que haya un objeto con scroll marcado con el atributo app:layout_behavior="@string/appbar_scrolling_view_behavior". Este debe declararse por encima de la App Bar.
  • La forma en que se afectan los hijos de App Bar se determina en app:layout_scrollFlags.

Las banderas de scroll controlan el tipo de comportamiento con que desaparece la Toolbar:

  • scroll: indica que un view desaparecerá al desplazar el contenido.
  • enterAlways:vuelve visible al view ante cualquier signo de scrolling.
  • enterAlwaysCollapsed: vuelve visible el view solo si se mantiene el scroll en la parte superior del contenido.
  • exitUntilCollapsed: desaparece el view hasta que sus dimensiones superen la altura mínima.

CollapsingToolbarLayout: Espacio Flexible De La App Bar

La App Bar es un elemento flexible capaz de aumentar o reducir su tamaño para desplegar mejor experiencia de usuario dependiendo de las interacciones que se ejerzan sobre ella.

Para implementar esta característica se usa el CollapsingToolbarLayout. Un layout especial que envuelve a la Toolbar para controlar las reacciones de expansión y contracción de los elementos que se encuentran dentro de un AppBarLayout.

Con reacciones me refiero a la variación de sus dimensiones y las desapariciones de la escena si es necesario.

Collapsing Toolbar: Ejemplo En Android Con Material Design

Para crear una App Bar con que contraiga, solo envuelves la Toolbar de la siguiente forma:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">


    <!-- Objeto con Scroll -->
    <android.support.v4.widget.NestedScrollView
        ...
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <!--Contenido desplazable -->

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

    <!-- App Bar -->
    <android.support.design.widget.AppBarLayout
        ...>

        <!-- Collapser -->
        <android.support.design.widget.CollapsingToolbarLayout
            ...
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <!-- Toolbar -->
            <android.support.v7.widget.Toolbar 
                ...
                app:layout_collapseMode="pin"/>

        </android.support.design.widget.CollapsingToolbarLayout>


    </android.support.design.widget.AppBarLayout>


</android.support.design.widget.CoordinatorLayout>

El código anterior contrae y expande la AppBar al momento de usar el scrolling. Para comprender el funcionamiento recuerda las siguientes instrucciones:

  • El CollapsingToolbarLayout debe envolver directamente a la Toolbar.
  • La Toolbar puede sobrevivir a la contracción del contenido a través del atributo app:layout_scrollMode="pin". Los demás elementos desaparecerán.
  • Puedes animar el titulo de la Toolbar añadiéndolo al CollapsingToolbarLayout con setTitle().
  • Al reducir un view puedes usar un efecto Parallax (es necesario aplicar el atributo app:layout_collapseMode="parallax") para difuminar su background con el color indicado en app:contentScrim .

Si quieres ahorrar tiempo implementando el Material Design en tus app Android, te recomiendo la plantilla Material Design App de Codecanyon

Crear Aplicación Android Con Toolbar

Paso #1: En Android Studio, crea un nuevo proyecto yendo a File > New Project. Llámalo “Citas Peligrosas”. Selecciona Blank Activity y confirma.

Paso #2: A continuación crea una nueva clase llamada DetailActivity.java. Esta actividad contendrá el detalle para cada ítem del Recycler View que se usará para mostrar la lista de chicas con las que queremos tener citas.

Paso #3: Agrega las dependencias del código de abajo al archivo build.gradle. Verás tres nuevas librerías: La librería de diseño de soporte antes mencionada; la librería Circle Image View que permite crear Image Views circulares y la librería Glide que nos permitirá cargar en segundo plano las imágenes.

build.gradle

dependencies {
    ...
    compile 'com.android.support:appcompat-v7:22.2.0'
    compile 'com.android.support:design:22.2.0'
    compile 'com.android.support:recyclerview-v7:22.2.0'
    compile 'com.android.support:cardview-v7:22.2.0'
    compile 'com.github.bumptech.glide:glide:3.6.0'
    compile 'de.hdodenhof:circleimageview:1.3.0'
}

Paso #4: Ve a res/values/strings.xml y agrega las siguientes cadenas:

strings.xml

<resources>
    <string name="app_name">Citas Peligrosas</string>

    <string name="action_settings">Settings</string>
    <string name="action_add">Añadir</string>
    <string name="action_favorite">Marcar Favorito</string>

    <string name="title_activity_detail">DetailActivity</string>

    <string name="girl_ipsum">
        Morbi posuere lorem nec elit dapibus, vitae accumsan mauris pellentesque.
        Vestibulum a lobortis mi. Vestibulum lacinia molestie semper. Pellentesque
        ultrices arcu lacus, interdum mollis nibh convallis ut. Aliquam sagittis
        sem a lacus gravida, eu accumsan purus volutpat. Donec tempus, est id
        tristique consectetur, lacus ligula dignissim turpis, cursus commodo turpis
        libero in purus. Phasellus tempus gravida tempor.
    </string>
    <string name="action_search">Buscar</string>
</resources>

Paso #5: Crea un nuevo archivo colors.xml en res/values y agrega los siguientes colores para el tema con Material Design:

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="darkPrimaryColor">#D32F2F</color>
    <color name="primaryColor">#F44336</color>
    <color name="accentColor">#448AFF</color>
</resources>

Paso #6: Lo siguiente es ir a res/values/dimens.xml y añadir las dimensiones de abajo.

dimens.xml

<resources>

    <dimen name="headerbar_elevation">4dp</dimen>
    <dimen name="card_margin">16dp</dimen>
    <dimen name="size_fab">56dp</dimen>
    <dimen name="fab_margin">16dp</dimen>
    <dimen name="avatar_size">40dp</dimen>

</resources>

Paso #7: Ahora crearemos el POJO de los objetos de la lista. Añade una nueva clase llamada Girl.java con el código de abajo. Este es el modelo de datos para los elementos del RecyclerView.

Girl.java

import java.util.Random;

/**
 * POJO de las chicas
 */
public class Girl {

    private String name;
    private int idDrawable;

    public Girl(String name, int idDrawable) {
        this.name = name;
        this.idDrawable = idDrawable;
    }

    public Girl(String name) {
        this.name = name;
        this.idDrawable = getRandomGirlDrawable();
    }

    public String getName() {
        return name;
    }

    public int getIdDrawable() {
        return idDrawable;
    }

    /**
     * Asigna un drawable en forma aleatoria
     *
     * @return id del drawable
     */
    private int getRandomGirlDrawable() {
        Random rnd = new Random();
        switch (rnd.nextInt(8)) {
            default:
            case 0:
                return R.drawable.girl1;
            case 1:
                return R.drawable.girl2;
            case 2:
                return R.drawable.girl3;
            case 3:
                return R.drawable.girl4;
            case 4:
                return R.drawable.girl5;
            case 5:
                return R.drawable.girl6;
            case 6:
                return R.drawable.girl7;
            case 7:
                return R.drawable.girl8;
        }
    }
}

Paso #8: Descarga y extrae las imágenes de este archivo. Luego pégalas en res/drawable. Estas permitirán crear las miniaturas y el detalle de cada ítem.

Paso #9: Añade otra clase con el nombre de Girls.java para representar una estructura de datos de prueba. Con ella se poblará el adaptador.

Los objetos que se usarán tendrán datos aleatorios. Por lo que verás un método llamado randomList() para cumplir esta función.

Girls.java

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Envoltura para generar una lista de ejemplo
 */
public class Girls {

    public static final String[] girlsNamesDummy = {
            "Catherine", "Evelyn", "Phyllis", "Beverly", "Michelle",
            "Denise", "Virginia", "Ruth", "Barbara", "Amanda", "Anna",
            "Catherine", "Melissa", "Sandra", "Julia", "Paula", "Kimberly",
            "Diane", "Betty", "Sharon", "Ruby", "Barbara", "Ann", "Phyllis",
            "Linda", "Marie", "Deborah", "Sara", "Gloria", "Karen", "Patricia",
            "Donna", "Catherine", "Louise", "Catherine", "Joyce", "Katherine",
            "Janice", "Cheryl", "Lisa", "Andrea", "Elizabeth", "Nicole",
            "Cynthia", "Angela", "Donna", "Deborah", "Sandra", "Cheryl", "Jane"
    };

    /**
     * Genera una lista de objetos {@link Girl} con un tamaño determinado
     *
     * @param count Tamaño
     * @return Lista aleatoria
     */
    public static List<Girl> randomList(int count) {
        Random random = new Random();
        List<Girl> items = new ArrayList<>();

        // Restricción de tamaño
        count = Math.min(count, girlsNamesDummy.length);

        while (items.size() < count) {
            items.add(new Girl(girlsNamesDummy[random.nextInt(girlsNamesDummy.length)]));
        }

        return new ArrayList<>(items);
    }
}

Paso #10: Modifica el layout de DetailActivity.java con la definición XML de abajo. Básicamente este diseño se compone de:

  • Un CoordinatorLayout para crear una Collapsing Toolbar.
  • Una imagen representativa dentro de la App Bar.
  • Cardviews para agregar secciones hipotéticas como: Perfil, Amigos e Intereses.
  • Un Floating Action Button relacionado a la app bar para un posible inicio de chat. La clase FloatingActionButton también hace parte de la librería de soporte.

Superimportante resaltar que el coordinator proporciona dos atributos para que el FAB (en general para todos los elementos flotantes)  flote con referencia a un view hermano.

  • layout_anchor: Es el view al que será anclado el FAB.
  • layout_anchorGravity: La ubicación del elemento. Puedes usar banderas como bottom, top, end, etc.

activity_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">


    <!-- Objeto con Scroll -->
    <android.support.v4.widget.NestedScrollView
        android:id="@+id/scroll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/app_bar"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:paddingTop="24dp">

            <!-- Card Perfil -->
            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/card_margin">

                <LinearLayout
                    style="@style/Widget.CardContent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
                    <!--Etiqueta Perfil -->
                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Perfil"
                        android:textAppearance="@style/TextAppearance.AppCompat.Title" />
                    <!--Texto de ejemplo-->
                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="@string/girl_ipsum" />

                </LinearLayout>

            </android.support.v7.widget.CardView>

            <!-- Card Amigos -->
            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/card_margin"
                android:layout_marginLeft="@dimen/card_margin"
                android:layout_marginRight="@dimen/card_margin">

                <LinearLayout
                    style="@style/Widget.CardContent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <!-- Etiqueta Amigos -->
                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Amigos(450)"
                        android:textAppearance="@style/TextAppearance.AppCompat.Title" />
                    <!-- Texto de ejemplo -->
                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="@string/girl_ipsum" />

                </LinearLayout>

            </android.support.v7.widget.CardView>

            <!-- Card Intereses -->
            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/card_margin"
                android:layout_marginLeft="@dimen/card_margin"
                android:layout_marginRight="@dimen/card_margin">

                <LinearLayout
                    style="@style/Widget.CardContent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <!-- Etiqueta Intereses -->
                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Intereses"
                        android:textAppearance="@style/TextAppearance.AppCompat.Title" />

                    <!-- Texto de ejemplo -->
                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="@string/girl_ipsum" />

                </LinearLayout>

            </android.support.v7.widget.CardView>

        </LinearLayout>

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

    <!-- App Bar -->
    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="256dp"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <!-- Collapser -->
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapser"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginEnd="64dp"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <!-- Imagen del detalle -->
            <ImageView
                android:id="@+id/image_paralax"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax" />

            <!-- Toolbar -->
            <android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="@android:color/transparent"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:theme="@style/CustomActionBar"

                />

        </android.support.design.widget.CollapsingToolbarLayout>


    </android.support.design.widget.AppBarLayout>

    <!-- FAB -->
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="@dimen/size_fab"
        android:layout_height="@dimen/size_fab"
        android:layout_margin="@dimen/fab_margin"
        android:src="@mipmap/ic_chat"
        app:borderWidth="0dp"
        app:elevation="@dimen/fab_elevation"
        app:layout_anchor="@id/app_bar"
        app:layout_anchorGravity="bottom|right|end" />


</android.support.design.widget.CoordinatorLayout>

Paso #11: Descarga los siguientes iconos para la Toolbar de la actividad de detalle. Al extraer el contenido, pega las carpetas en res.

Paso #12: Ahora debes actualizar el menú de tu actividad de detalle. Ve a res/menu/detail.xml y pega el código de abajo. Solo tendremos tres action buttons para añadir, agregar a favoritos y los ajustes:

detail.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.herprogramacion.toolbarapp.DetailActivity">
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        app:showAsAction="never" />
    <item
        android:id="@+id/action_add"
        android:icon="@mipmap/ic_person_add"
        android:orderInCategory="1"
        android:title="@string/action_add"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_favorite"
        android:icon="@mipmap/ic_favorite"
        android:orderInCategory="2"
        android:title="@string/action_favorite"
        app:showAsAction="ifRoom" />
</menu>

Paso #13: Abre DetailActivity.java y agrega el código de abajo.  El objetivo es cargar los datos de la chica que vienen desde el recycler  y setearlos en cada view.

Si te fijas, en las acciones de los action buttons mostramos mensajes con la llamada del método showSnackBar().  Su función crear un nuevo elemento de la librería de diseño llamado SnackBar con la cadena especificada. Este componente es muy parecido a los Toasts, solo que permiten añadir una acción en su contenido.

DetailActivity.java

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;

import com.bumptech.glide.Glide;

public class DetailActivity extends AppCompatActivity {


    private static final String EXTRA_NAME = "com.herprogramacion.toolbarapp.name";
    private static final String EXTRA_DRAWABLE = "com.herprogramacion.toolbarapp.drawable";

    /**
     * Inicia una nueva instancia de la actividad
     *
     * @param activity Contexto desde donde se lanzará
     * @param title    Item a procesar
     */
    public static void createInstance(Activity activity, Girl title) {
        Intent intent = getLaunchIntent(activity, title);
        activity.startActivity(intent);
    }

    /**
     * Construye un Intent a partir del contexto y la actividad
     * de detalle.
     *
     * @param context Contexto donde se inicia
     * @param girl    Identificador de la chica
     * @return Intent listo para usar
     */
    public static Intent getLaunchIntent(Context context, Girl girl) {
        Intent intent = new Intent(context, DetailActivity.class);
        intent.putExtra(EXTRA_NAME, girl.getName());
        intent.putExtra(EXTRA_DRAWABLE, girl.getIdDrawable());
        return intent;
    }

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

        setToolbar();// Añadir action bar
        if (getSupportActionBar() != null) // Habilitar up button
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        Intent i = getIntent();
        String name = i.getStringExtra(EXTRA_NAME);
        int idDrawable = i.getIntExtra(EXTRA_DRAWABLE, -1);

        CollapsingToolbarLayout collapser =
                (CollapsingToolbarLayout) findViewById(R.id.collapser);
        collapser.setTitle(name); // Cambiar título

        loadImageParallax(idDrawable);// Cargar Imagen

        // Setear escucha al FAB
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        showSnackBar("Opción de Chatear");
                    }
                }
        );


    }

    private void setToolbar() {
        // Añadir la Toolbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

    /**
     * Se carga una imagen aleatoria para el detalle
     */
    private void loadImageParallax(int id) {
        ImageView image = (ImageView) findViewById(R.id.image_paralax);
        // Usando Glide para la carga asíncrona
        Glide.with(this)
                .load(id)
                .centerCrop()
                .into(image);
    }

    /**
     * Proyecta una {@link Snackbar} con el string usado
     *
     * @param msg Mensaje
     */
    private void showSnackBar(String msg) {
        Snackbar
                .make(findViewById(R.id.coordinator), msg, Snackbar.LENGTH_LONG)
                .show();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.detail, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        switch (id) {
            case R.id.action_settings:
                showSnackBar("Se abren los ajustes");
                return true;
            case R.id.action_add:
                showSnackBar("Añadir a contactos");
                return true;
            case R.id.action_favorite:
                showSnackBar("Añadir a favoritos");
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }


    }
}

Paso #14: Lo siguiente es crear nuestro layout para los ítems de la lista. Dirígete a res/layout y crea un nuevo layout llamado list_item.xml. La idea es representar una sola línea de texto con un avatar.

list_item.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="wrap_content"
    android:gravity="center_vertical"
    android:paddingBottom="8dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="8dp">

    <!-- Image circular de la librería externa -->
    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/avatar"
        android:layout_width="@dimen/avatar_size"
        android:layout_height="@dimen/avatar_size"
        android:layout_marginRight="16dp" />

    <!-- Nombre de la chica -->
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/list_item_textview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?attr/textAppearanceListItem" />

</LinearLayout>

Paso #15: Crea una nueva clase llamada SimpleAdapter.java. Esta clase es un RecyclerView.Adapter personalizado que inflará el nombre y avatar de cada chica desde list_item.xml.

SimpleAdapter.java

import android.app.Activity;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.List;

public class SimpleAdapter extends RecyclerView.Adapter<SimpleAdapter.SimpleViewHolder>
        implements ItemClickListener {
    private final Context context;
    private List<Girl> items;

    public static class SimpleViewHolder extends RecyclerView.ViewHolder
            implements View.OnClickListener {
        // Campos respectivos de un item
        public TextView nombre;
        public ImageView avatar;
        public ItemClickListener listener;

        public SimpleViewHolder(View v, ItemClickListener listener) {
            super(v);

            nombre = (TextView) v.findViewById(R.id.list_item_textview);
            avatar = (ImageView) v.findViewById(R.id.avatar);
            this.listener = listener;
            v.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            listener.onItemClick(v, getAdapterPosition());
        }
    }

    public SimpleAdapter(Context context, List<Girl> items) {
        this.context = context;
        this.items = items;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    @Override
    public SimpleViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.list_item, viewGroup, false);
        return new SimpleViewHolder(v, this);
    }

    @Override
    public void onBindViewHolder(SimpleViewHolder viewHolder, int i) {
        Girl currentItem = items.get(i);
        viewHolder.nombre.setText(currentItem.getName());
        Glide.with(viewHolder.avatar.getContext())
                .load(currentItem.getIdDrawable())
                .centerCrop()
                .into(viewHolder.avatar);
    }

    /**
     * Sobrescritura del método de la interfaz {@link ItemClickListener}
     *
     * @param view     item actual
     * @param position posición del item actual
     */
    @Override
    public void onItemClick(View view, int position) {
        DetailActivity.createInstance(
                (Activity) context, items.get(position));
    }


}


interface ItemClickListener {
    void onItemClick(View view, int position);
}

Paso #16: Ahora es el turno del layout de MainActivity.java. Ve a res/layout/activity_main.xml y añade la siguiente definición. Habrá una App Bar con espacio flexible que se reducirá al desplazar el recycler view.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.CoordinatorLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/coordinator"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--Objeto Con Scroll -->
        <android.support.v7.widget.RecyclerView
            android:id="@+id/reciclador"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/coordinator"
            android:padding="3dp"
            android:scrollbars="vertical"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

        <!-- App Bar -->
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

            <!-- Toolbar -->
            <android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|enterAlways"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

        </android.support.design.widget.AppBarLayout>


    </android.support.design.widget.CoordinatorLayout>
</RelativeLayout>

Paso #17: Ya para finalizar, abre MainActivity.java y realiza los siguientes cambios:

  • Añade la Toolbar (puedes crear un método llamado setToolbar() para ello).
  • Quita el título de la app bar y setealo en el CollapsingToolbarLayout.
  • Crea el adaptador y setealo en el RecyclerView.

MainActivity.java

import android.os.Bundle;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {


    private RecyclerView recycler;
    private LinearLayoutManager lManager;
    private CollapsingToolbarLayout collapser;

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

        setToolbar();// Añadir la Toolbar

        SimpleAdapter adaptador = new SimpleAdapter(this, Girls.randomList(30));

        // Obtener el Recycler
        recycler = (RecyclerView) findViewById(R.id.reciclador);
        recycler.setHasFixedSize(true);

        // Usar un administrador para LinearLayout
        lManager = new LinearLayoutManager(this);
        recycler.setLayoutManager(lManager);

        // Crear un nuevo adaptador

        recycler.setAdapter(adaptador);


    }

    private void setToolbar() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
            case R.id.action_search:de
                showSnackBar("Comenzar a buscar...");
                return true;
            case R.id.action_settings:
                showSnackBar("Se abren los ajustes");
                return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * Proyecta una {@link Snackbar} con el string usado
     *
     * @param msg Mensaje
     */
    private void showSnackBar(String msg) {
        Snackbar
                .make(findViewById(R.id.coordinator), msg, Snackbar.LENGTH_LONG)
                .show();
    }
}

Paso #18: Ejecuta la aplicación en Android Studio:

Toolbar En Android: CoordinatorLayout y CollapsingToolbarLayout

Conclusión

¿Pudiste implementar la Toolbar en Android?

Bien, en este tutorial has visto los pasos necesarios para crear una interfaz simple pero visualmente atractiva.

La librería de soporte de diseño de Google es el gran avance que necesitábamos para comenzar con el Material Design de forma robusta. No obstante aún tiene varios bugs, por lo que podemos esperar que mejore con el tiempo.

Recuerda actualizar el SDK de Android para estar al tanto de las mejoras y obtener el funcionamiento completo de las librerías de soporte.

Ahora el paso a seguir es comprender como crear un Navigation Drawer en Android con el nuevo widget para Material Design.

  • Dj-dixole Chupin

    hola muy buenos tutoriales me ahn ayudado muchisimo ,, sera qe puede hacer un tuto en donde utilize la api SIP para poder realizar llamadas o rebirlas a travez del wifi, muchas gacias

  • Dj-dixole Chupin

    hola muy buenos tutoriales me ahn ayudado muchisimo ,, sera qe puede hacer un tuto en donde utilize la api SIP para poder realizar llamadas o rebirlas a travez del wifi, muchas gacias

  • VipPunkJoshers Droopy

    exelente gracias.

  • icaiza

    Por que sucede esto… Me deja un pequeño pedazo del texto e icono que tengo.. Mi codigo es el siguiente.. Alguna idea

    /* CODIGO */

    • icaiza

      Me respondo.. El problema es que tenía aplicado un theme con
      @android:color/transparent
      El statusBar era transparente y motivo por el cual se ve parte del toolbar.. Sólo quite este item del theme y Listo

  • KEVIN JAVIER VEGA MERINO

    Hola , excelente pagina, tengo un problema al integrar varios elementos y donde debo de meter un swipe to refresh para que no choque con el scroll de toda la vista, entonces como puedo acomodarlo y tambien un tablayout con viewpager, el xml es:

    <android.support.design.widget.CoordinatorLayout
    <android.support.v4.widget.NestedScrollView

    ¿Donde puedo meter el swipe to refresh layout para q no choque con el scroll de los
    elementos y necesito codigo tambien para que no choque el scroll?
    ¿quiero meter un tablayout con viewpager abajo del cardview que puse, solo quiero que
    cambie el contenido dentro de los tabs y el cardview debe quedar asi como esta fijo?

    Necesito de tu ayuda amigo jejej. Saludos

  • Mario Enrique Gonzalez Hernand

    Hola!, acabo de conocer tu blog… Y LO AMO!
    gracias por este contenido! :)

    • :D Gracias Mario!, para ti que eres nuevo, publico 4 tutoriales al mes, así que ten paciencia por si ves algo de demoras. Saludos!

      • Francisco Briceño

        Hola, mira tengo una duda, como puedo lograr un toolbar o un navbar pero que sea en button, gracias!

  • Tengo una consulta, a ver si con tu super conocimientos alienigenas jeje puedes ayudarme.

    Sucede que estoy metiendole un mapa al AppBarLayout, y se hace extramadamente lento moverte por el mapa, principalmente cuando intentas moverte verticalmente. Porqueno no sacarlo de ahi y ponerlo en un RelativeLayout te preguntaras, pero es que necesito que este ahi ya que quiero el efecto de cuando haces scroll hacia abajo se oculte con el fade de la transicion nativa de Android.

    Paso el codigo:

    Muchas gracias como siempre!

    • Solucionado con un touch event :)

  • dev

    Buenos tardes James

    Estoy teniendo problema al tratar de ver el detalle de la imagen , en la parte que me esta dando el problema “java.lang.ClassCastException: android.app.Application cannot be cast to android.app.Activity” es DetailActivity.createInstance((Activity) context, items.get(position));

    no entiendo porque me esta pasando eso .gracias

    • dev

      Buenos dias James, Ya lo resolvi le agrege la siguiente linea intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) en el metodo createInstance.

      ejemplo

      public static void createInstance(Context activity, Item title) {
      Intent intent = getLaunchIntent(activity, title);
      intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Agrege esta linea
      activity.startActivity(intent);
      }

  • Julio Alvarado

    Hola gracias por el Tuto, una duda: al correr el proyecto se me cierra en el celular, y el error q me sale es en el MainActiviy en el método de setToolbar(), me podrían ayudar?

    • Hola Julio que tipo de error sale?

      • Julio Alvarado

        Hola gracias por contestar, me sale q no es compatible la toolbar, pero no entiendo q no es compatible con que

        • Qué tal si revisas el import de la toolbar que estás usando. Si usas AppCompatActivity entonces usa el paquete android.support.v7.widget.Toolbar

          • Julio Alvarado

            Sale, voy a checar, gracias amigo

  • Enmanuel

    Buenos dias , como puedo hacer lo inverso mostar el toolbar al ver el detalle y al momento de hacer el scroll up mantener la imagen y el icono activo y ocultar el toolbar.

    gracias

    • Hola Enmanuel. Supongo que solo quitas el collapsing layout y luego usas las banderas de scroll que se ve en la actividad de lista

  • Eliot .Net

    Que tal buenas tardes.
    Pregunta se puede incluir un fragmento que muestre el mapa dentro de un GLIDE asi como se realiza con la imagen. Que en vez que sea una imagen muestre en un mapa la ubicación en mi caso de un cliente a visitar.

    • Cuando hablas de mapa te refieres a una imagen o la API de google maps?

      • Eliot .Net

        Efectivamente. Google maps, lo que me interesa es decirle al vendedor cuando entre a un cliente a visita le ponga la marca de donde esta ubicado dicho cliente.

        • Creo que es posible compañero. Espero hacer un tutorial sobre google maps para ver si podemos incluir algo parecido.

      • CarlosCarEd

        Hola tengo un problema al compilar me sale:

        Error:(31, 28) No resource found that matches the given name (at ‘style’ with value ‘@style/Widget.CardContent’).

        • Hola carlos. Descarga el proyecto y revisa el archivo styles. Allí se encuentra ese estilo que te están diciendo que falta.

  • Enmanuel

    Saludo , estoy teniendo un incoveniente al tratar de correger la aplicacion me presenta el siguiente android.view.InflateException: Binary XML file line #2: Error inflating class android.support.design.widget.CoordinatorLayout ,

    Porque sera ?

    • Hola, tienes incluida la libreria de diseño de soporte en el build.gradle?

      • Enmanuel

        gracias , ya me funciono

  • Rogelio Trejo

    Cuando instalo la aplicación en mi emulador, corre perfectamente, muestra todo como debería, pero en el menú no muestra el icono de la aplicación, pero en las apps si aparece como instalada.
    Y en mi celular solo se instala pero no se ejecuta y del icono ni hablar.

    Alguna solución al respecto?

    • Te arroja algún error?

      • Rogelio

        Me faltaban unas lineas en el manifest, pero ya quedó todo solucionado :D muchas gracias

        • :D ese es el problema con algunos de mis tutoriales, que doy por hecho que ya todos saben como hacerlo. Tengo pendiente modificar este tuto para añadirle ese paso.

          Saludos!

  • Bluedp

    Estimados, primero que todo estoy muy feliz de haber descubierto este blog porque esta buenísimo ! segundo (Argentina), como dato al margen probé la app en mi phone (4.4.2) pero tuvo problemas con los iconos del Toobar que no se visualizaban, lo solucione actualizando la librería de soporte a “compile’com.android.support:design:22.2.1′”. aun así no queda bien del todo, agregar esta ok pero el corazón aparece el setting, espero google solucione a la brevedad esto, por lo menos muestra avances en cada versión. Saludos !

  • Luis

    Hola excelente articulo :) me encanto sin duda alguna muy buenos aportes gracias por esos aportes, bien tengo una pequeña duda espero puedan ayudarme yo trate de hacer el este ejemplo pero co un valor estático pero no se por que no me funciona el botón de regresar me podrías ayudar por favor a decirme donde estoy mal mi código lo tengo así

    public class Quines extends AppCompatActivity {

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

    setToolbar();// Añadir action bar
    if (getSupportActionBar() != null) // Habilitar up button
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    CollapsingToolbarLayout collapser = (CollapsingToolbarLayout) findViewById(R.id.collapser);
    collapser.setTitle(“¿Quienes Somos? “); // Cambiar título

    }

    private void setToolbar() {
    // Añadir la Toolbar
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    }

    }

    y lo mismo que tienes en el activity_delait.txt solo que en la en ImageView tengo un valor estático de la siguiente forma:

    al correr la aplicación si funciona el único problema que tengo es el botón de regresar no funciona. me podrías ayudar por favor a decirme que hice mal!

    • Hola amigo.

      Pues intenté cambiando a medida absoluta como tienes, pero igual me funcionó. Tanto desde el up button como del back button.

      ¿Dentro del método onOptionsItemSelected estás usando la opción android.R.id.home?

      Si es así, tal vez eso esté interfiriendo.

  • lucas

    cuando voy hasta abajo de todo en el listado, y luego subo desaparece el toolbar, cuando entro en una ficha y regreso al listado aparece nuevamente, que puede ser?

    • James Revelo

      Hola lucas.

      Esto se debe a que tal vez cambiaste las banderas de scroll de la toolbar. ¿Probaste dejar presionado el scroll hacia abajo para que apareciera?

      • Rafael Valencia

        Hola, un estupendo tutorial sin duda alguna. También tengo el mismo problema, he descargado el código, ejecutado la aplicación y todo funciona pero ocasionalmente se presenta el problema(hacer scroll hasta abajo, luego subir y en lugar del toolbar solamente hay un espacio en blanco.)

        • Hola Rafael, ¿has probado actualizar el sdk para tener la nueva versión de la librería de diseño?

          • Rafael Valencia

            Hola, lo he hecho ya, pero el problema persiste.

          • Mmm… lo estás ejecutando en un emulador con 5.1 para arriba?

          • Raverito Stone

            Efectivamente probe con versiones desde la 4.4 hasta la 6.0 y a partir del 5.1 corre bien con versiones anteriores hay problemas para visualizar el toolbar al deslizar la lista hasta el ultimo y tambien al ver detalle de contacto los iconos del toolbar estan fuera del margen.

  • FreddyASG

    Saludos, muy buen tutorial, gracias.

    Hay un detalle cuando corres la App en APIs 21 o superior, el toolbar desaparece… o se monta con el statusbar.

    en APIs inferiores funciona perfecto.

    • James Revelo

      Hola Freddy.

      Eso también me pasaba, solo tienes que actualizar el SDK y los emuladores para que funcione. Al parecer la librería todavia tiene bugs.

      Saludos!

  • Rene

    Buenismo este tutorial. en verdad gracias, solo tengo un par de dudas. Probe el codigo en un android 4.4.2 y no aparecen los menus de la actividad detalle. No soporta estos nuevos elementos? Tampoco aplica el color del DarkPrimaryColor y no estoy muy seguro el por qué de esto

    • James Revelo

      Hola Rene.

      Creo que está pasando debido a que la librería aún tiene bugs.

      El darkprimarycolor se supone se aplica si usas el atributo en el estilo sin ponerle el prefijo “android:”.

      • Rene

        debe ser eso, haré otro proyecto para ver si el problema se resuelve. Gracias

        • James Revelo

          Dale :D

  • Rafel

    Por lo que veo, soy el único que me pasa. Cuando pulso en algún nombre la aplicación se cierra. Lo he hecho todo, a parte, me he bajado el código y nada que no hay manera. Alguna explicación?

    • James Revelo

      Hola Rafel. ¿Que error te sale en el log cat?

      • Rafel

        Ninguno, cuando pulso en el nombre, sale:
        Se a detenido la aplicación citas peligrosas

        • James Revelo

          Mm…pero en el logcat debe aparecer el registro de alguna excepción.

          • Rafel

            Muchas gracias, por responder me faltaba:

            en el Manifest

          • James Revelo

            Ammm… :D , creo que debo incluir ese paso también.

  • Rey

    Esta muy bueno el tuto! pero la inquietud que tengo es que se debe modificar en las Clases Girl.java y Girls.java para que la lista y las imagenes no sean aleatorias sino que el primer nombre del Pojo tome la primera imagen. el segundo la segunda y asi sucesivamente! Gracias de ante Mano!

    • James Revelo

      Hola REY.

      Debes usar el otro constructor de Girl.java. Este recibe el nombre y el drawable respectivo.

      Dentro de Girls.java tienes que eliminar el bucle while que añade a la lista los elementos. Allí llenas manualmente con el método add() los elementos.

      • Rafelcf

        cual es el otro constructor?

  • Luis

    Saludos, muy buen tutorial, me gustaria que las Imagenes no fueran Aleatorias. alguna idea?

    • James Revelo

      Hola Luis. Puedes leer datos reales desde un servidor (web service) para poblar la lista.

      Saludos!

  • Alejandro

    Hola,
    ¿Cómo puedo hacer para cambiar la toolbar cuando se cambie el fragmento? Quero cambiar tanto el color como los iconos.

    Gran tutorial, estaba deseando un tutorial así, espero ver más tutoriales de Material Design como este.
    Un saludo.

    • James Revelo

      Hola Alejandro.
      Debes usar el método setHasOptionsMenu(true) dentro del método onCreate() del fragmento que vaya a cambiar la action bar. Si tienes otro menú, entonces lo inflas en el método onCreateOptionsMenu y procesas los eventos en . Es lo mismo que hacemos en las actividades.

      Saludos !

  • Andrew

    Gran trabajo y muchísimas gracias. Solo una pregunta, no puedo ver los layouts, me salta un error diciendo “Use View.isInEditMode() in your custom views to skip code or show sample data when shown in the IDE”, le cambio de api(19, 21, 22) pero sigue el mensaje. Es normal ese comportamiento en el Android Studio o tengo que configurarlo de alguna forma para poder visualizar los layouts antes de ejecutar el programa. Un saludo muy cordial.

    • James Revelo

      Hola Andrew.

      Si es normal. Es un bug. A mi también me pasa, sin embargo algunas veces funciona si cambias el tema por Material.Light en la preview.

  • Carlos

    gracias excelente!!!

    • James Revelo

      Me alegra que te sirva Carlos :D

  • holtech

    hola empezar felicitando los tutoriales que están muy buenos … queria consultar si se puede utilizar material design en aplicaciones android que no necesariamente sean compatibles con la version API 21 de android como la API 19

    • James Revelo

      Hola holtech. Si es posible, ya que se usan librerías de soporte. Aunque algunas características no son posibles, la mayoría de aspectos se conservan.

  • Gonza

    Muchas gracias por este tutorial amigo. Estuve pasando un mal momento pero ya vuelvo a la carga!
    Muchas gracias.

  • Gonza

    Muchas gracias por el tutorial James. Anduve pasando mal momento de salud pero ya entraré manos a la obra nuevamente. Sludos

    • James Revelo

      :D Que bueno que ya recuperaste.

      Saludos!