Diseño Android: ActionBar con Toolbar

Última actualización : 09/09/2017

android

La ActionBar es la barra superior presente en la inmensa mayoría de aplicaciones y habitualmente suele mostrar el nombre de la pantalla visualizada, el icono de la aplicación (o el del menú de navegación) y las acciones disponibles.

action bar

La ActionBar forma parte del profundo rediseño acometido por Google para Android 3/4 y su adopción fue bastante rápida. Para poder utilizarla en Android 2 era necesario recurrir a implementaciones propias o de terceros como la popular ActionBarSherlock pero en 2013 se incluyó en las librería de compatibilidad.

Con Lollipop llegó Material Design y un nuevo rediseño visual tan importante como el que supuso la llegada de Android 4. Una de las novedades ha sido la aparición de la Toolbar que pretende ser un reemplazo de la ActionBar más potente y flexible. Mientras que la ActionBar es un elemento del sistema que se muestra en una Activity si se hereda de un tema que la incluya, Toolbar es simplemente un widget que aporta grandes ventajas:

  • Puede ubicarse en cualquier lugar.
  • Se puede ubicar más de una en la misma pantalla.
  • Es un ViewGroup por lo que se pueden incluir dentro la Toolbar otros widgets.
  • Fácilmente adaptable al tamaño de pantalla.
  • Sigue proporcionado la funcionalidad de la ActionBar facilitando por tanto su adopción.

En este tutorial veremos las funcionalidades básicas de ActionBar utilizando como implementación la Toolbar. En futuros artículos y tips se seguirán explorando las funcionalidades de ActionBar/Toolbar:

Cursos aplicaciones móviles

Creación de la ActionBar con Toolbar

El widget Toolbar forma parte de Android 5 (API 21) pero ha sido incluido en la librería de compatibilidad para que pueda ser utilizado en cualquier versión de Android. En este artículo se utilizará la Toolbar proporcionada por la librería de compatibilidad y el proyecto de ejemplo requerirá como mínimo Android Jelly Bean (API 16).

Los pasos para utilizar una Toolbar a modo de ActionBar son los siguientes:

  1. Incluir la última versión de la librería de compatibilidad en el proyecto (módulo AppCompat). En Android Studio\Gradle se hace en el fichero /app/build.gradle
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:26.0.0'
    }
    

    A partir de la versión 26, debemos asegurar que tenemos configurado el repositorio maven de Google.

    allprojects {
        repositories {
            jcenter()
            maven {
                url "https://maven.google.com"
            }
        }
    }
    

    Para más información sobre la importación de librerías en Eclipse ADT y Android Studio, consultar este artículo.

  2. Utilizar como tema base de la aplicación uno que no incluya la ActionBar (Theme.AppCompat.NoActionBar, Theme.AppCompat.Light.NoActionBar). Para ello se define en el AndroidManifest.xml el tema propio de la aplicación
    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity ...
    

    y se crea ese tema en el fichero /app/src/main/res/values/styles.xml el cual a su vez heredará del tema adecuado de la librería de compatibilidad que además aplicará por defecto estilos de Material Design. Por ello, debemos indicar como mínimo el color principal de la aplicación que será el que tendrá nuestra toolbar.

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="colorPrimary">@color/primary</item>
            <item name="colorPrimaryDark">@color/primaryDark</item>
            <item name="android:textColorPrimary">@color/textColorPrimary</item>
        </style>
    
    </resources>    
    
    
    
  3. Añadir la Toolbar a la pantalla. Puesto la Toolbar se utilizará como ActionBar hay que asegurar que se muestre siempre en la parte superior de la pantalla y que tenga el tamaño predeterminado por el sistema.

    Se aplicará el tema ThemeOverlay.AppCompat.Dark.ActionBar para hacer que los textos tengan color blanco, o bien ThemeOverlay.AppCompat.ActionBar para color negro. Este color se aplicará también para los iconos que se muestren en la Toolbar siempre que sean una transparencia alpha, siendo este el caso de los iconos proporcionados por el sistema para el menú overflow (“los tres puntos”) o el botón de volver (“la flecha”).

    Con el atributo elevation se establece la sombra de la Toolbar en la parte inferior aunque esta sólo se visualizará en dispositivos con Lollipop.

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:minHeight="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:elevation="@dimen/elevation"       
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            tools:ignore="UnusedAttribute"/>
    
    </LinearLayout>
    
  4. La Activity debe heredar de AppCompatActivity y establecer la toolbar que hemos definido como implementación de la ActionBar (setSupportActionBar). Por defecto la ActionBar mostrará como título el label definido en el AndroiManifest.xml para la Activity (o la aplicación si este no se define) pero se puede personalizar el título utilizando el método setTitle de tal modo que si el parámetro es nulo no se mostrará título alguno. También se puede definir un “subtítulo” con el método setSubtitle.
    package toolbar.android.danielme.com.toolbardemo;
    
    import android.os.Bundle;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.View;
    
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
            //getSupportActionBar().setTitle(null);
    
        }
    
    }
    

El resultado en Lollipop es el siguiente:

simple android toolbar

Icono de aplicación y botón “subir”

Originalmente a la izquierda de la ActionBar se mostraba el icono de la aplicación y en el caso de que no estemos en la pantalla principal la pulsación de este icono permite al usuario subir un nivel en la jerarquía de pantallas (lo cual por cierto no siempre equivale a pulsar el botón atrás de Android). Para indicar que esta funcionalidad está habilitada se muestra junto al icono una flecha.

android actionbar icons

Sin embargo, en Material Design el icono de la aplicación no debe mostrarse según las guías de estilo y, en el caso del botón para subir un nivel, simplemente se visualiza una flecha cuyo icono ya está incluido en la librería de compatibilidad. Este icono se habilita en la definición de la Toolbar:

    <android.support.v7.widget.Toolbar
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/toolbar"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:minHeight="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:elevation="@dimen/elevation"
        app:navigationIcon="?attr/homeAsUpIndicator"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        tools:ignore="UnusedAttribute"/>

También se puede habilitar de forma programática:

getSupportActionBar().setDisplayHomeAsUpEnabled(true);

En la Activity tendremos que implementar el listener correspondiente para atender la pulsación.

package toolbar.android.danielme.com.toolbardemo;

import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        //getSupportActionBar().setTitle(null);

        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, R.string.up, Toast.LENGTH_SHORT).show();
            }
        });

    }

}

Si simplemente queremos volver atrás, podemos prescindir de la implementación de este listener e indicar en el manifest dentro de la definición de la Activity cual es la Activity “padre”.

<meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".OtherActivity" />

simple android toolbar

Reutilizar la Toolbar

La ActionBar implementada como una Toolbar será utilizada en prácticamente todas las Activity de nuestra app con exactamente la misma definición así que sería muy práctico poder reutilizarla fácilmente. Una posible solución es utilizar el sistema de reutilización de layouts con include. Esto podemos aplicarlo en el proyecto de ejemplo definiendo la toolbar en su propio XML.

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

<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/toolbar"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:minHeight="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:elevation="@dimen/elevation"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    tools:ignore="UnusedAttribute"/>

Ahora tan sólo tenemos que importar este XML en los layout donde sea necesaria la Toolbar.

    <include
        android:id="@+id/toolbar"
        layout="@layout/toolbar" />

Menú de acciones

En la ActionBar se proporcionan al usuario las acciones que puede realizar desde cada pantalla concreta. Estas acciones se muestran como un icono, un texto, un icono con un texto o dentro de un menú desplegable.

Las acciones de una ActionBar se definen como un menú estándar de Android por lo que son análogos a los menús que en Android 2 se desplegaban en la parte inferior de pantalla al pulsar el botón físico del Menú. Vamos a añadir a la ActionBar del proyecto de ejemplo las siguientes acciones:

  • Un icono “añadir” que se deberá mostrar siempre.
  • Un icono de edición que se muestra sólo si hay espacio en la ActionBar.
  • Un menú desplegable con tres entradas: Settings, Help y About.

Seguimos los siguientes pasos:

  1. Definir el menú en el fichero /app/src/main/res/menu/main_activity_menu.xml. Cada acción será un elemento de tipo item.
    <?xml version="1.0" encoding="utf-8"?>
    <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=".MainActivity">
    
        <item
            android:id="@+id/action_add"
            android:icon="@mipmap/ic_action_new"
            android:title="@string/action_add"
            app:showAsAction="always" />
    
        <item
            android:id="@+id/action_edit"
            android:icon="@mipmap/ic_action_edit"
            android:title="@string/action_edit"
            app:showAsAction="ifRoom" />
        
    
        <!-- MENU -->
    
        <item
            android:id="@+id/action_settings"
            android:title="@string/action_settings"
            app:showAsAction="never" />
    
        <item
            android:id="@+id/action_help"
            android:title="@string/action_help"
            app:showAsAction="never" />
    
        <item
            android:id="@+id/action_about"
            android:title="@string/action_about"
            app:showAsAction="never" />
    </menu>
    
  2. En el atributo showAsAction indicar el comportamiento visual de cada acción:
    • always: muestra siempre el botón de la acción aunque no haya espacio. Si se quiere destacar una acción principal para la pantalla Material Design recomienda utilizar el elemento “Floating Action Button”.
    • ifRoom: si hay espacio se muestra el botón de la acción, en caso contrario se visualiza en el menú.
    • never: nunca muestra el botón de la acción en la barra por lo que se muestra siempre en el menú.
    • withText: muestra un texto con el título de la acción y si se indicó el icono muestran ambos. Esta opción se suele combinar con otras, por ejemplo ifRoom|withText muestra el texto junto al icono si ambos caben y en caso contrario muestra sólo el icono, si el icono tampoco cabe entones la acción se muestra en el menú.
    • collapseActionView: la acción muestra un layout personalizado, por ejemplo un EditText.
  3. Indicar el tema visual del menú desplegable en la definición de la Toolbar.
        <android.support.v7.widget.Toolbar
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/toolbar"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:minHeight="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/Theme.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.ActionBar" />
    
  4. Implementar en la Activity el método onCreateOptionsMenu para indicar el menú a utilizar. Este es también el momento para ocultar de forma dinámica alguna entrada del menú si fuera necesario.
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.main_activity_menu, menu);
            return super.onCreateOptionsMenu(menu);
        }
    
  5. Definir la acción a realizar para cada elemento del menú en el método onOptionsItemSelected.
       @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
                case R.id.action_add:
                    action(R.string.action_add);
                    return true;
                case R.id.action_edit:
                    action(R.string.action_edit);
                    return true;
                case R.id.action_settings:
                    action(R.string.action_settings);
                    return true;
                case R.id.action_help:
                    action(R.string.action_help);
                    return true;
                case R.id.action_about:
                    action(R.string.action_about);
                    return true;
                default:
                    return super.onOptionsItemSelected(item);
            }
        }
    
        private void action(int resid) {
            Toast.makeText(this, getText(resid), Toast.LENGTH_SHORT).show();
        }
    
    

Este es el resultado en una pantalla de gran resolución.

toolbar actionbutton 2

En una pantalla de menor resolución en la que no hay espacio para mostrar la acción de edición ésta se muestra en el menú desplegable.

toolbar actionbutton

Configurar el menú desde un fragment

Si componemos las interfaces gráficas mediante fragments, práctica muy recomendable, podemos añadir y configurar acciones en la Toolbar desde cualquier fragment. Un caso típico de esta situación es que tangamos un ViewPager y las acciones de la Toolbar dependan del fragment del ViewPager que se esté mostrando en pantalla. Es lo que sucede en mi app Muspy for Android tal y como muestra la siguiente imagen.

Para utilizar menús en los fragments seguiremos los siguientes pasos.

  1. Habilitar el uso del menú en el fragment llamando al método setHasOptionsMenu en el método onCreateView o onCreate.
  2. Hacer el “inflado” del menú sobreescribiendo el método onCreateOptionsMenu. A pesar de tener el mismo nombre que el equivalente que hemos visto para la Activity, es ligeramente distinto.
     @Override
      public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.fragment_actions_menu, menu);
      }
    
  3. Implementar las acciones de cada item del menú sobreescribiendo el método onOptionsItemSelected que es igual al que vimos para la Activity.

Todos los menús que mostremos será combinados en uno solo, esto es, tendremos a la vez el menú que pongamos en la Activity y los de los fragments. Si fuera necesario definir cierto orden para el conjunto de acciones de toda la combinación podemos hacerlo con el parámetro “android:orderInCategory” en la definición de cada item en los xml de los menús.

Personalizar el menú desplegable (overflow)

Tal y como se aprecia en la anterior captura, el menú desplegable (overflow) cubre casi toda la ActionBar. Ese es el comportamiento definido por Material Design pero en versiones previas de Android el menú se mostraba debajo de la ActionBar. Si queremos recuperar este comportamiento tendremos que jugar con dos estilos propios de los menús de tipo popup:

  • overlapAnchor: boolean que indica si el menú debe cubrir el botón que lo muestra. Por omisión es true.
  • dropDownVerticalOffset: desplazamiento vertical del menú con respecto a la posición en que se muestra por omisión.

Con overlapAnchor = false el menú aparece debajo del botón pero ocupa una pequeña parte de la ActionBar tal y como puede verse en la siguiente captura

toolbar overflow menu

Por ello, si queremos que el menú se muestre justo debajo de la ActionBar debemos aplicar también dropDownVerticalOffset. Su valor lo he calculado probando por lo que no es fiable, y además depende de la orientación del dispositivo.

Para personalizar el menú desplegable con estas propiedades en la aplicación de ejemplo hay que realizar los siguientes cambios:

  1. En el layout eliminamos el atributo app:popupTheme de la Toolbar ya que este estilo lo vamos a aplicar directamente desde el tema de la aplicación.
  2. En /res/styles.xml, definimos el estilo anterior y, a su vez, su propiedad actionOverflowMenuStyle que será otro estilo definido en el mismo fichero. Este último estilo contiene las dos propiedades que nos interesa modificar.
    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:tools="http://schemas.android.com/tools">
    
        <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="colorPrimary">@color/primary</item>
            <item name="colorPrimaryDark">@color/primaryDark</item>
            <item name="android:textColorPrimary">@color/textColorPrimary</item>
            <item name="popupTheme">@style/PopupTheme</item>
        </style>
    
        <style name="PopupTheme" parent="Theme.AppCompat.Light">
            <item name="actionOverflowMenuStyle">@style/OverflowMenu</item>
            <!--https://github.com/danielme-com/-Android-popup-menu- -Support-library/blob/master/res/values/styles.xml-->
        </style>
    
        <!-- displays the overflow menu below the toolbar-->
        <style name="OverflowMenu" parent="Widget.AppCompat.Light.PopupMenu.Overflow">
            <item name="android:dropDownVerticalOffset">-4dp</item>
            <item name="overlapAnchor">false</item>
        </style>
    
    </resources>
    
  3. Lamentablemente la configuración de estas propiedades depende de la versión de Android y de la orientación del dispositivo por lo que tendremos que crear tres nuevos ficheros de estilo:
    • /res/values-v21/styles.xml: Api 21+ (Lollipop) en orientación vertical
      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <style name="OverflowMenu" parent="Widget.AppCompat.Light.PopupMenu.Overflow">
              <item name="android:overlapAnchor">false</item>
              <item name="android:dropDownVerticalOffset">4dp</item>
          </style>
      </resources>
      
    • /res/values-land-v21/styles.xml: Api 21+ en horizontal
      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <style name="OverflowMenu" parent="Widget.AppCompat.Light.PopupMenu.Overflow">
              <item name="android:overlapAnchor">false</item>
              <item name="android:dropDownVerticalOffset">8dp</item>
          </style>
      </resources>
      
    • /res/values-land/styles.xml: Versiones inferiores a Api 21 en horizontal
      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <style name="OverflowMenu" parent="Widget.AppCompat.Light.PopupMenu.Overflow">
              <item name="android:dropDownVerticalOffset">1dp</item>
              <item name="overlapAnchor">false</item>
          </style>
      </resources>
      

Tras la aplicación de estos cambios obtendremos el siguiente resultado:

toolbar overflow menu

Con respecto al resto de propiedades de estilo del menú overflow, se aplican tal y como se muestran en el tutorial Diseño Android:Popup Menu.

Pestañas

Ver el tutorial Diseño Android: Toolbar y Pestañas con Material Design.

Código de ejemplo

El proyecto completo se encuentra disponible en GitHub. Para más información sobre cómo utilizar GitHub, consultar este artículo.

Master Pyhton, Java, Scala or Ruby

13 Responses to Diseño Android: ActionBar con Toolbar

  1. Sixto says:

    Muy buen tutorial, felicitaciones. Me gustaría saber si hay una forma de alinear el menú overflow con la parte inferior del toolbar?

    Gracias de antemano…

  2. Sixto says:

    Gracias, me ha servido de mucha ayuda este tutorial.

  3. Omar Bautista says:

    excelente, gracias por el aporte

  4. Laura says:

    Es genial, sabrias decirme como hacer el efecto con las pestañas como en el actionbar swipe

    • danielme.com says:

      Se puede conseguir una toolbar con pestañas con estilo Material Design usando un layout con la siguiente estructura y basado en los widgets de las support library:

      <android.support.design.widget.AppBarLayout >

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

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

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

      <android.support.v4.view.ViewPager/>

      Puedes ver una implementación de ejemplo en https://github.com/chrisbanes/cheesesquare

  5. Rodrigo says:

    Hola … quiero hacer una app que haga uso de botones y de un toolbar, creo el layout, pero en el .java quiero hacer uso de la opciones del ToggleButton y si extendiendo AppCompatActivity me bloquea el código relacionado al ToggleButton y si extiendo Activity me bloquea lo relacionado al actionBar…

  6. Wandy says:

    Excelente amigo, muchas gracias por tu ayuda!!!

  7. Joaquín says:

    He estado viendo tutoriales sobre este tema y este es el más claro hasta ahora, pero por desgracia, cumpliendo al pie de la letra con el código que ha subido a GitHub, no me sale el desplazamiento del menú desplegable. Es como si overlapAnchor estuviera en true en el Theme y tampoco funcionara el desplazamiento vertical dropDownVerticalOffset. Le he estado dando vueltas y no se me ocurre nada. Si me pudieras orientar porqué te lo agradecería mucho.

    • danielme.com says:

      En el código de GitHub para aplicar el desplazamiento basta con descomentar en el /values/styles.xml la siguiente línea:
      <!–@style/OverflowMenu–>

  8. Christians says:

    Excelente manera de explicar las cosas. Fácil de entender

  9. Demian Rincon Cañaveral says:

    Como podria alternar 2 toolbar dependiendo del evento ejemplo una toolbar por defecto y al ocurrir el evento que cambie por otra?

  10. Andres says:

    Muchas gracias, informacion muy valiosa

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: