Diseño Android: Tarjetas con CardView

android

Las tarjetas son un componente gráfico popularizado por Google Now y que ahora forman parte de Material Design. El concepto de tarjeta es el de una ficha de papel o portada de una carpeta que proporciona sólo el resumen de cierto contenido y permite acceder al contenido completo. El resumen puede estar compuesto por un conjunto de elementos heterogéneos que incluye imágenes, títulos, textos, íconos, menús contextuales, acciones…

Android SDK proporciona una implementación del componente tarjeta con el widget CardView en un módulo propio de la librería de compatibilidad. En este tutorial diseñaremos una tarjeta más o menos típica con este componente haciendo uso también del widget Toolbar.

En primer lugar, debemos incluir en nuestro proyecto la librería que proporciona la CardView. En Android Studio:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.1'
    compile 'com.android.support:cardview-v7:21.0.0+'
}

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

CardView es una subclase de FrameLayout por lo que podemos considerar este widget como un Layout más cuyos hijos se pueden superponer. Vamos a crear una tarjeta que simplemente muestre una imagen del olinguito. El layout contiene además una Toolbar a modo de ActionBar y la tarjeta ha sido incluída en un ScrollView.

<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 xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        android:minHeight="?attr/actionBarSize"
        app:theme="@style/ThemeOverlay.AppCompat.ActionBar" />

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp">

        <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
            android:id="@+id/card"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            card_view:cardCornerRadius="4dp"
            card_view:cardBackgroundColor="@android:color/white"
            card_view:cardUseCompatPadding="true"
            card_view:cardElevation="4dp">
            <!--By Mark Gurney [CC BY 3.0 (http://creativecommons.org/licenses/by/3.0)], via Wikimedia Commons -->
            <ImageView
                android:id="@+id/imageView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/olinguito"
                android:adjustViewBounds="true"
               />

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

    </ScrollView>

</LinearLayout>

El resultado que tenemos es simplemente el ImageView aunque obsérvese los bordes redondeados y la sombra pertenecientes a la CardView.

android cardview demo

Toolbar

Vamos a añadir una cabecera a la tarjeta que muestre un título, un subtítulo y un un menú desplegable con varias acciones utilizando para ello una Toolbar. La tarjeta ahora contendrá un LinearLayout con la Toolbar y el ImageView.

<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
            android:id="@+id/card"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            card_view:cardBackgroundColor="@android:color/white"
            card_view:cardCornerRadius="4dp"
            card_view:cardElevation="4dp"
            card_view:cardUseCompatPadding="true">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbarCard"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@android:color/white"
                    app:popupTheme="@style/Theme.AppCompat.Light"
                    app:subtitleTextAppearance="@style/Card.Subtitle"
                    app:theme="@style/ToolbarCard"
                    app:titleTextAppearance="@style/Card.Title" />

                <!--By Mark Gurney [CC BY 3.0 (http://creativecommons.org/licenses/by/3.0)], via Wikimedia Commons -->
                <ImageView
                    android:id="@+id/imageView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:adjustViewBounds="true"
                    android:src="@drawable/olinguito" />

            </LinearLayout>

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

Para personalizar la Toolbar se han creado tres nuevos estilos: el de la propia Toolbar (no aplicaremos el estilo utilizado en la Toolbar que actúa como ActionBar), el del texto de título y el texto de subtítulo.

<?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>

    <style name="ToolbarCard" parent="ThemeOverlay.AppCompat.ActionBar">
        <item name="colorPrimary">@android:color/black</item>
        <item name="android:textColorPrimary">@android:color/black</item>
    </style>

    <style name="Card.Title" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
        <item name="android:textSize">24sp</item>
    </style>

    <style name="Card.Subtitle" parent="TextAppearance.Widget.AppCompat.Toolbar.Subtitle">
        <item name="android:textSize">16sp</item>
        <item name="android:textColor">@color/textGrey</item>
    </style>

</resources>

El menú se define como es habitual en el xml correspondiente. En nuestro caso queremos que las opciones se muestren en un menú contextual (showAsAction tendrá el valor never), pero también se podrían mostrar la acciones del mismo modo que en cualquier Toolbar, por ejemplo con iconos.

<?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_option1"
        android:title="@string/option1"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_option2"
        android:title="@string/option2"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_option3"
        android:title="@string/option3"
        app:showAsAction="never" />
</menu>

En la Activity es donde se establece el título, subtítulo y menú de la Toolbar de la tarjeta. También asignamos a la Toolbar un listener de tipo Toolbar.OnMenuItemClickListener para atender las pulsaciones de las acciones.

package com.danielme.android.cardview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
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);

        Toolbar toolbarCard = (Toolbar) findViewById(R.id.toolbarCard);
        toolbarCard.setTitle(R.string.title);
        toolbarCard.setSubtitle(R.string.subtitle);
        toolbarCard.inflateMenu(R.menu.menu_card);
        toolbarCard.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                 switch (item.getItemId()) {
                    case R.id.action_option1:
                        Toast.makeText(MainActivity.this, R.string.option1, Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.action_option2:
                        Toast.makeText(MainActivity.this, R.string.option2, Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.action_option3:
                        Toast.makeText(MainActivity.this, R.string.option3, Toast.LENGTH_SHORT).show();
                        break;
                }
                return true;
            }
        });
    }

}

android cardview and toolbar

Secciones expandibles

Veamos cómo añadir una sección expandible tipo acordeón que haga uso de animaciones para mostrar y ocultar el contenido.

Añadimos dos nuevos layout a la tarjeta: la cabecera de la sección expandible (título e icono) y la información que se mostrará al expandirse la sección.

android collapsible layout

   <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
            android:id="@+id/card"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            card_view:cardBackgroundColor="@android:color/white"
            card_view:cardCornerRadius="4dp"
            card_view:cardElevation="4dp"
            card_view:cardUseCompatPadding="true">

            <LinearLayout
                android:id="@+id/linearLayoutCardContent"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbarCard"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@android:color/white"
                    app:popupTheme="@style/Theme.AppCompat.Light"
                    app:subtitleTextAppearance="@style/Card.Subtitle"
                    app:theme="@style/ToolbarCard"
                    app:titleTextAppearance="@style/Card.Title" />

                <!--By Mark Gurney [CC BY 3.0 (http://creativecommons.org/licenses/by/3.0)], via Wikimedia Commons -->
                <ImageView
                    android:id="@+id/imageView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:adjustViewBounds="true"
                    android:contentDescription="@string/olinguito"
                    android:src="@drawable/olinguito" />

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:onClick="toggleDetails"
                    android:orientation="horizontal"
                    android:padding="8dp">

                    <TextView
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:layout_weight="1"
                        android:gravity="center_vertical"
                        android:text="@string/details"
                        android:textColor="@android:color/black"
                        android:textSize="20sp" />

                    <ImageView
                        android:id="@+id/imageViewExpand"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:background="@android:color/transparent"
                        android:contentDescription="@string/details"
                        android:src="@mipmap/more" />
                </LinearLayout>


                <LinearLayout
                    android:id="@+id/linearLayoutDetails"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical"
                    android:paddingLeft="8dp"
                    android:visibility="gone">

                    <TextView
                        android:id="@+id/textViewInfo"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/info"
                        android:textColor="@android:color/black"
                        android:textSize="16sp" />
                </LinearLayout>


            </LinearLayout>

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

La pulsación de la cabecera (todo el layout) está asociada al método toggleDetails cuyo cometido es mostrar u ocultar el linearLayoutDetails según su estado actual. Esta acción se realizará cambiando la visibilidad del layout de GONE a VISIBLE y viceversa, pero para que el resultado final sea refinado y elegante vamos a utilizar animaciones:

  • Si el linearLayoutDetails está oculto, lo mostraremos deslizándolo hacia abajo empezando en la cabecera. También se cambiará el icono con la flecha rotándolo sobre sí mismo 180º
  • En caso contrarío, las animaciones se realizarán de forma inversa de tal modo que el linearLayoutDetails se deslice hacia arriba hasta desaparecer debajo de la tarjeta y el icono con la flecha “vuelva” a su posición anterior

La animación del icono flecha es muy sencilla pero el deslizado del layout resulta más complejo de lo que puede aparecer a simple vista ya que es necesario calcular la altura que debe ocupar el layout a medida que se va deslizando y además hay que tener en cuenta que su contenido tiene una altura variable (en el ejemplo se utiliza un TextView que puede tener múltiples líneas). Para esta animación de deslizado voy a utilizar este código que he refactorizado con algunos pequeños cambios en la clase ExpandAndCollapseViewUtil reutilizable para cualquier proyecto.

El nuevo código necesario en la MainActivity es el siguiente (no olvidar obtener las vistas en el onCreate)

private ViewGroup linearLayoutDetails;
private ImageView imageViewExpand;
private static final int DURATION = 250;

 public void toggleDetails(View view) {
        if (linearLayoutDetails.getVisibility() == View.GONE) {
            ExpandAndCollapseViewUtil.expand(linearLayoutDetails, DURATION);
            imageViewExpand.setImageResource(R.mipmap.more);
            rotate(-180.0f);
        } else {
            ExpandAndCollapseViewUtil.collapse(linearLayoutDetails, DURATION);
            imageViewExpand.setImageResource(R.mipmap.less);
            rotate(180.0f);
        }
    }

    private void rotate(float angle) {
        Animation animation = new RotateAnimation(0.0f, angle, Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        animation.setFillAfter(true);
        animation.setDuration(DURATION);
        imageViewExpand.startAnimation(animation);
    }

El siguiente video muestra el resultado final del proyecto de ejemplo.


Código de ejemplo

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

One Response to Diseño Android: Tarjetas con CardView

  1. Lorena dice:

    Buenas, a la hora de incluir las dependencias me da error, y no se como solucionarlo, si me pudieras ayudar lo agradecería mucho.
    Muchas gracias, un saludo.

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: