Cómo crear CardViews personalizados en Android

19-10-2017 - Andrés Cruz

Cómo crear CardViews personalizados en Android In english

Este material forma parte de mi curso y libro completo; puedes adquirirlos desde el apartado de libros y/o cursos.

En esta entrada veremos como crear CardViews personalizados, para ello solo nos enfocaremos en crear la vista y no trabajaremos en ningún otro componente de la aplicación para Android (como la actividad) ni nada por el estilo, la idea es centrarnos en cómo crear un diseño completo para un listado que pueda contar con varias opciones, algo de información básica y un campo de destacado.

Los CardView son excelentes contenedores para mostrar datos de manera resumida y detallada; son muy empleados para crear los items de los listados mediante los Lo nuevo del Material Design: Los RecyclerView.

Como puedes ver en la imagen promocional, existen ciertos elementos alineados de una manera que a primera vista puede no resultar tan fácil de deducir su organización pero que en realidad no es compleja su construcción.

Resumiendo un poco e indicando cada uno de los elementos en la siguiente imagen de manera descendente, nuestra vista contará con 3 secciones principales:

  • Sección destacada.
  • El CardView como tal.
  • La sección de opciones.
cardview estructura en android

¿Para que sirve un CardView?

Los CardView son un elemento de diseño que cuentan con un diseño similar a un widget de manera nativa con el típico sombreado y bordes redondeados y son muy empleados en conjunto con los RecicleView que ya hemos tratado en una anterior entrada.

Como se ha mencionado anteriormente, contiene una seria de propiedades unicas como el redondeo de los border, una propiedad para cambiar el color de fondo etc; puedes consultar la documentación oficial para más información Crear listas y tarjetas.

Creando nuestro CardView

Finalmente se procede a detallar los pasos necesarios para crear el CardView anterior; como contenedor padre al CardView definiremos un RelativeLayout; la razón, su facilidad para alinear elementos con contenido flotantes en prácticamente cualquier posición.

Dentro del mismo contenedor RelativeLayout definiremos 3 bloques más:

  • Otro contenedor padre dentro del CardView que será un RelativeLayout para el manejo del margen superior.
  • Otro contenedor para la sección destacada del CardView (TextView) que permitirá alinear el TextView de manera central.
  • Un LinearLayout que contendrá algunos botones flotantes con el propósito del manejo de la sección de opciones permitidas pora el usuario.

Creando la sección de opciones

En este bloque definimos un margen para dar el típico efecto de que la sección destacada sobresale del CardView (mitad dentro del contenedor y mitad fuera) y el LinearLayout con la propiedad android:orientation="horizontal" permite alinear todos los elementos contenidos dentro del mismo de manera horizontal.

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/rlContainer"
        android:layout_centerHorizontal="true"
        android:layout_marginRight="@dimen/fab_margin"
        android:layout_marginTop="-15dp"
        android:orientation="horizontal">

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fabInfo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="@dimen/fab_margin"
            android:src="@drawable/ic_action_info_outline"
            app:fabSize="mini" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fabEdit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_action_edit"
            app:fabSize="mini" />
    </LinearLayout>

Creando la sección destacada

Ahora crearemos la sección destacada que consta de un TextView centrado; para ello, el contenedor padre RelativeLayout debe ocupar todo el ancho y en conjunto con la propiedad android:layout_centerHorizontal="true" permite alinear el TextView en el medio, de aquí le ponemos un padding de 10dp para hacerlo el globo un poco más vistoso y un margen que nos permite tener centrado nuestra sección destacada.

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="25dp">

        <TextView
            android:id="@+id/tvMount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:background="@drawable/layout_bg_corner_mount_1"
            android:fontFamily="sans-serif-thin"
            android:padding="10dp"
            android:text="title"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:textColor="#FFFFFF"
            android:textSize="25dp" />
    </RelativeLayout>

Diseñando el resto del CardView

Ahora lo último que nos falta es crear la sección central y principal de nuestro listado cuyo código no tiene complicación alguna y no necesita mayor información:

    <RelativeLayout
        android:id="@+id/rlContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="0dp"
        android:layout_marginLeft="@dimen/space_component3x"
        android:layout_marginRight="@dimen/space_component3x"
        android:layout_marginTop="0dp">

        <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:card_view="http://schemas.android.com/apk/res-auto"
            android:id="@+id/cvContainer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/card_detail_mount_margin"
            android:foreground="?android:attr/selectableItemBackground"
            card_view:cardCornerRadius="5dp">

            <LinearLayout
                android:id="@+id/llContainer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_vertical"
                android:orientation="vertical"
                android:padding="@dimen/pad_10dp">

                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <TextView
                        android:id="@+id/tvCount"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentLeft="true"
                        android:fontFamily="sans-serif-light"
                        android:text="title"
                        android:textAppearance="?android:attr/textAppearanceSmall"
                        android:textColor="#FFFFFF"
                        android:textStyle="bold" />

                    <TextView
                        android:id="@+id/tvDate"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentRight="true"
                        android:fontFamily="sans-serif-light"
                        android:gravity="right"
                        android:text="title"
                        android:textAppearance="?android:attr/textAppearanceSmall"
                        android:textColor="#FFFFFF"
                        android:textStyle="bold" />
                </RelativeLayout>

                <TextView
                    android:id="@+id/tvTitle"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="@dimen/pad_5dp"
                    android:layout_marginTop="@dimen/space_component3x"
                    android:layout_toLeftOf="@+id/tvDate"
                    android:fontFamily="sans-serif-condensed"
                    android:text="title"
                    android:textColor="#FFFFFF"
                    android:textSize="20dp" />

                <TextView
                    android:id="@+id/tvDirection"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:fontFamily="sans-serif-light"
                    android:text="title"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:textColor="#FFFFFF"
                    android:textSize="@dimen/abc_text_size_small_material" />

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/space_big"
                    android:orientation="vertical" />

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

    </RelativeLayout>

Finalmente el código completo:

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

    <RelativeLayout
        android:id="@+id/rlContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="0dp"
        android:layout_marginLeft="@dimen/space_component3x"
        android:layout_marginRight="@dimen/space_component3x"
        android:layout_marginTop="0dp">

        <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:card_view="http://schemas.android.com/apk/res-auto"
            android:id="@+id/cvContainer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/card_detail_mount_margin"
            android:foreground="?android:attr/selectableItemBackground"
            card_view:cardCornerRadius="5dp">

            <LinearLayout
                android:id="@+id/llContainer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_vertical"
                android:orientation="vertical"
                android:padding="@dimen/pad_10dp">

                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <TextView
                        android:id="@+id/tvCount"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentLeft="true"
                        android:fontFamily="sans-serif-light"
                        android:text="title"
                        android:textAppearance="?android:attr/textAppearanceSmall"
                        android:textColor="#FFFFFF"
                        android:textStyle="bold" />

                    <TextView
                        android:id="@+id/tvDate"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentRight="true"
                        android:fontFamily="sans-serif-light"
                        android:gravity="right"
                        android:text="title"
                        android:textAppearance="?android:attr/textAppearanceSmall"
                        android:textColor="#FFFFFF"
                        android:textStyle="bold" />
                </RelativeLayout>

                <TextView
                    android:id="@+id/tvTitle"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="@dimen/pad_5dp"
                    android:layout_marginTop="@dimen/space_component3x"
                    android:layout_toLeftOf="@+id/tvDate"
                    android:fontFamily="sans-serif-condensed"
                    android:text="title"
                    android:textColor="#FFFFFF"
                    android:textSize="20dp" />

                <TextView
                    android:id="@+id/tvDirection"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:fontFamily="sans-serif-light"
                    android:text="title"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:textColor="#FFFFFF"
                    android:textSize="@dimen/abc_text_size_small_material" />

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/space_big"
                    android:orientation="vertical" />

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

    </RelativeLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/rlContainer"
        android:layout_centerHorizontal="true"
        android:layout_marginRight="@dimen/fab_margin"
        android:layout_marginTop="@dimen/negative_margin_design_fab_size_mini"
        android:orientation="horizontal">

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fabInfo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="@dimen/fab_margin"
            android:src="@drawable/ic_action_info_outline"
            app:fabSize="mini" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fabEdit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_action_edit"
            app:fabSize="mini" />
    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/space_component5x">

        <TextView
            android:id="@+id/tvMount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:background="@drawable/layout_bg_corner_mount_1"
            android:fontFamily="sans-serif-thin"
            android:padding="@dimen/space_component2x"
            android:text="title"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:textColor="#FFFFFF"
            android:textSize="@dimen/card_detail_mount_text_size" />
    </RelativeLayout>
</RelativeLayout>

Valores del dimen.xml

  • <dimen name="space_component">5dp</dimen>
  • <dimen name="space_component2x">10dp</dimen>
  • <dimen name="space_component3x">15dp</dimen>
  • <dimen name="space_component5x">25dp</dimen>
  • <dimen name="fab_margin">10dp</dimen>
  • <dimen name="space_big">20dp</dimen>
  • <dimen name="card_detail_mount_margin">45dp</dimen>
  • <dimen name="card_detail_mount_text_size">25dp</dimen>

Recursos y colores

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/colorFive"/>
    <stroke android:width="3dp" android:color="@color/colorFive" />
    <corners android:radius="30dp"/>
    <padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>
<color name="colorFive">#F7C51E</color>

(Opcional) Tipografías en Android

Para darle un poco de estilo, y como puedes ver el las secciones de código presentado anteriormente, se cambió la tipografía cuando se consideraba hacerlo; las siguientes tipos de tipografía que podemos emplear en Android:

android:fontFamily="sans-serif"           // roboto regular
android:fontFamily="sans-serif-light"     // roboto light
android:fontFamily="sans-serif-condensed" // roboto condensed
android:fontFamily="sans-serif-black"     // roboto black
android:fontFamily="sans-serif-thin"      // roboto thin (android 4.2)
android:fontFamily="sans-serif-medium"    // roboto medium (android 5.0)

Representados en la siguiente imagen:

Tipografía en android

Puedes ver la explicación completa en el siguiente enlace: service. How to change fontFamily of TextView in Android.


Andrés Cruz
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.

Conozca nuestros cursos sobre Laravel, CodeIgniter, Flutter, Electron, Django, Flask y muchos más!

Ver los cursos
¡Hazte afiliado en Gumroad!

!Cursos desde!

4$

En Academia

Ver los cursos

!Libros desde!

1$

Ver los libros
!Web Alojada en Hostinger!