Añadir Un ConstraintLayout En Android Studio

El ConstraintLayout es un descendiente de ViewGroup, cuyo fin es permitirte posicionar y redimensionar views de forma flexible a partir de una gran variedad de reglas de restricción.

Apariencia de ConstraintLayout en superficie Blueprint

Las restricciones son similares al concepto de posicionamiento relativo del RelativeLayout, no obstante el ConstraintLayout posee muchos más tipos de condicionamientos para crear diseños más libres.

Sumándole a lo anterior, el editor de layouts de Android Studio provee una inmensa cantidad de interacciones visuales para explotar al máximo los atributos del layout.

En este primer tutorial de la guía verás como:

  • Añadir un ConstraintLayout a tu proyecto
  • Convertir un layout a ConstraintLayout
  • Comprender el manejo del editor de layouts con el ConstraintLayout

Ejemplo De ConstraintLayout

Comenzaremos la exploración del ConstraintLayout con el diseño para presentar un tutorial de Develou. Esto involucra el posicionamiento del título, descripción, categoría, fecha de publicación, imagen destacada y dos botones:

Ejemplo de App con ConstraintLayout

Puedes descargar el código final del proyecto desde el siguiente botón:


Añadir Un ConstraintLayout En Nuevo Proyecto

Una de las formas más sencillas de incorporar el ConstraintLayout es a través de la creación de un nuevo proyecto y el uso de las plantillas base de actividades en Android Studio.

En nuestro caso procederemos de la siguiente forma:

Paso 1. Abre Android Studio y crea un nuevo proyecto. Selecciona la plantilla Empty Activity para la actividad principal

Plantilla Empty Activity en Android Studio

Paso 2. Nombra al proyecto «ConstraintLayout» y presiona Finish.

Android Studio añadirá automáticamente el artefacto androidx.constraintlayout en su ultima versión como dependencia a build.gradle del módulo:

dependencies {

    //...
    implementation 'androidx.constraintlayout:constraintlayout:ultima-version'
}

Si el proyecto es antiguo y aún no usas este componente, entonces copia y pega la anterior línea en tu archivo.

Verás que el layout creado automáticamente trae consigo como raíz a un ConstraintLayout y un TextView centrado en su interior:

ConstraintLayout en la pestaña Design
ConstraintLayout creado por Empty Activity

La definición XML del archivo es:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Puntos a tener en cuenta:

  • Al igual que los demás ViewGroups, es necesario especificar los atributos android:layout_width y android:layout_height para el ConstraintLayout
  • También puedes ver atributos del tipo app:layout_constraint*_to*Of. Estos representan a las restricciones de posicionamiento relativo, las cuales indican el identificador al que estará ligado el lado del view. Debes especificar al menos una restricción horizontal y vertical para cada view, de lo contrario se te marcará un error
  • Como el TextView está centrado en el padre, se usa el valor parent para especificar que los cuatro lados estarán asociados a los de su padre.

Editor De Layouts Y ConstraintLayout

Si abres el archivo activity_main.xml y seleccionas la pestaña Design, verás el editor de layouts con dos tipos de superficies: Design y Blueprint.

Select Design Surface

La superficie Design mostrará el detalle del contenido de los elementos agregados y Blueprint la omitirá.

Adicionalmente, existe una barra de herramientas con múltiples acciones que veremos en los siguientes tutoriales.

Toolbar de ConstraintLayout
Barra de herramientas asociada al ConstrainLayout

Por otro lado, podrás ver las restricciones de posición en la ventana Attributes con el nombre de Constraints:

Añadir Restricciones

Para probar el uso del editor de layouts sobre el ConstraintLayout, creemos restricciones de posicionamiento con el diseño propuesto al inicio.

Veamos:

Paso 1. Ubícate en la ventana Palette y arrastra los views requeridos: 5 TextViews, un ImageView y 2 Buttons.

Agregar views a un ConstraintLayout

Al añadir los views, la ventana Component Tree irá mostrándote los nuevos hijos de la jerarquía. En nuestro caso, esta quedaría así:

Paso 2. A partir de los límites de cada view, representados por círculos en los lados, arrastra una línea de conexión hacia otro elemento con el que deseas que sea condicionado.

Por ejemplo, la imagen destacada va en la parte superior, por lo que alineamos sus bordes superior , izquierdo, derecho con el padre:

Esta edición visual añadirá automáticamente los 3 atributos para las restricciones especificadas por la conexión anterior:

<ImageView
    android:id="@+id/post_featured_image"
    android:layout_width="wrap_content"
    android:layout_height="244dp"
    android:contentDescription="@string/featured_image_desc"
    android:src="@drawable/featured_image"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

Ahora veamos como hacerlo con el título. Este debe alinear su borde superior con el borde inferior de la imagen destacada y sus laterales con el padre:

Restricciones de posicionamiento entre Views

En este caso, el valor para la restricción borde-superior->borde-inferior tomarán el ID de la imagen destacada post_featured_image:

<TextView
    android:id="@+id/post_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Añadir Un ConstraintLayout En Android Studio"
    android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/post_featured_image" />

Intenta añadir las restricciones de los views restantes teniendo en cuenta esta organización:

  • Autor: Por debajo del título y alineado a la izquierda del padre
  • Fecha de publicación: Por debajo del autor y alineado a la izquierda del padre
  • Categoría: Acotado a la derecha del padre y por debajo de la imagen destacada
  • Contenido: Por debajo de la fecha de publicación y extendido horizontalmente en el padre . Acotado por los botones.
  • Botones: Acotados al borde inferior del padre y el lateral correspondiente

El diseño final se vería así:

Ejemplo de ConstraintLayout en Android

Y la restricciones en la definición XML son:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/post_featured_image"
        android:layout_width="wrap_content"
        android:layout_height="244dp"
        android:contentDescription="@string/featured_image_desc"
        android:src="@drawable/featured_image"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/post_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Añadir Un ConstraintLayout En Android Studio"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/post_featured_image" />

    <TextView
        android:id="@+id/post_author"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="James Revelo"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
        android:textStyle="bold"
        app:layout_constraintEnd_toStartOf="@+id/post_category"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/post_title" />

    <TextView
        android:id="@+id/post_publish_date"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="22/07/2021"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
        app:layout_constraintEnd_toEndOf="@+id/post_author"
        app:layout_constraintStart_toStartOf="@+id/post_author"
        app:layout_constraintTop_toBottomOf="@+id/post_author" />

    <TextView
        android:id="@+id/post_category"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="UI"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
        app:drawableLeftCompat="@drawable/ic_brush"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/post_title" />

    <TextView
        android:id="@+id/post_content"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:scrollbars="vertical"
        android:text="@string/text_content"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
        app:layout_constraintBottom_toTopOf="@+id/next_button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/post_publish_date" />

    <Button
        android:id="@+id/previous_button"
        style="@style/Widget.MaterialComponents.Button.TextButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Anterior"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/next_button"
        style="@style/Widget.MaterialComponents.Button.TextButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Siguiente"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Aunque las restricciones de posición son muy intuitivas, las estudiaremos en más detalle en el siguiente tutorial.


Convertir Layout A ConstraintLayout

También es posible convertir una layout existente en ConstraintLayout. Android Studio limpiará la jerarquía antigua de atributos innecesarios, inferirá restricciones equivalentes al orden anterior (si es posible) y buscará eliminar layouts anidados .

Pongamos como ejemplo una versión del layout del tutorial, donde el elemento raíz es un LinearLayout como se ve a continuación:

En su interior habrán otros dos LinearLayouts anidados para soportar la horizontalidad de la sección del autor y la de botones:

LinearLayouts anidados

Su definición XML se vería así:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    android:padding="16dp">

    <ImageView
        android:id="@+id/post_featured_image"
        android:layout_width="wrap_content"
        android:layout_height="244dp"
        android:contentDescription="@string/featured_image_desc"
        android:src="@drawable/featured_image" />

    <TextView
        android:id="@+id/post_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Añadir Un ConstraintLayout En Android Studio"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" />

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

        <TextView
            android:id="@+id/post_author"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="James Revelo"
            android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/post_category"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="UI"
            android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
            app:drawableLeftCompat="@drawable/ic_brush" />
    </LinearLayout>

    <TextView
        android:id="@+id/post_publish_date"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="22/07/2021"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />

    <TextView
        android:id="@+id/post_content"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:scrollbars="vertical"
        android:text="@string/text_content"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <Button
            android:id="@+id/previous_button"
            style="@style/Widget.MaterialComponents.Button.TextButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Anterior" />

        <Button
            android:id="@+id/next_button"
            style="@style/Widget.MaterialComponents.Button.TextButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Siguiente" />
    </LinearLayout>

</LinearLayout>

Así que los pasos para convertirlo serán:

Paso 1. Ve a la pestaña Design del layout

Paso 2. Ubícate en la ventana Component Tree, luego haz click derecho en el layout raíz y selecciona Convert LinearLayout to ConstraintLayout

Convert LinearLayout to ConstraintLayout

Paso 3. Al presionar esta opción, se desplegará una ventana que te permite elegir dos configuraciones:

Ventana Convert to ConstraintLayout

Si marcas Flatten Layout Hierarchy los layouts anidados serán removidos.

En el caso de Don’t flatten layouts referenced by id from other files evita que se transformen layouts cuya jerarquía tenga atributos android:id referenciados desde tus archivos de código.

Paso 4. Al presionar OK se comenzará el proceso de conversión y podrás ver el resultado final:

La conversión eliminó los dos layouts anidados y los atributos android:layout_weight. Sin embargo no hubo una inferencia de restricciones, por lo que tendremos que añadir los límites para cada view y así desaparezcan los errores.


Tipos De Restricciones

En este tutorial aprendiste varias formas de añadir un ConstraintLayout a tus proyectos Android Studio y el conocimiento básico para crear restricciones de posicionamiento.

No obstante, elementos como márgenes, tamaño, alineamiento, expansión, etc. necesitan ser atendidos. Por ello, el siguiente paso será profundizar en los tipos de restricciones y su representación en el editor de layouts.

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!