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.
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:
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
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:
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
yandroid:layout_height
para elConstraintLayout
- 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 valorparent
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.
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.
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.
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:
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í:
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:
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
Paso 3. Al presionar esta opción, se desplegará una ventana que te permite elegir dos configuraciones:
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.