En este tutorial te explicaré el uso de más básicas restricciones del ConstraintLayout
como lo son: restricciones de posicionamiento relativo, de márgenes, de centrado y de dimensión.
La idea es comprender los atributos que representan estas restricciones y cómo podemos aplicarlos desde el editor de layouts en Android Studio.
Ejemplo De Restricciones Del ConstraintLayout
Tomaremos como ilustración el layout que diseñamos en el tutorial anterior sobre el detalle de un tutorial de Develou, para mejorar el aspecto con las restricciones que veremos:
Descarga el código del proyecto Android Studio desde el siguiente enlace. El layout nuevo tendrá el nombre de p2_restricciones.xml
.
Posicionamiento Relativo
Las restricciones de posicionamiento relativo te permiten posicionar un view a partir de una declaración que haga referencia a otro view o al padre.
Como viste en el anterior tutorial, es posible restringir vertical y horizontalmente, lo que permite plena libertad de enlazar cualquier lado de un widget con el de otro.
Añadir Restricción De Posicionamiento
Para añadir una restricción de posicionamiento, selecciona el view desde la pestaña Design. Luego haz click en un círculo de restricción y arrastra hacia un punto de anclaje de otro view para establecer la conexión:
Otra forma de hacerlo es seleccionar el view y luego ir a la sección Layout de la ventana Attributes. Allí verás representados sus límites y las restricciones existentes. Si haces click en los botones que no tienen restricciones, Android Studio creará automáticamente la conexión con el widget más cercano:
Eliminar Restricción De Posicionamiento
Ahora bien, con la misma mecánica se realiza la eliminación de la restricción. Desde el lienzo selecciona el view y luego la restricción. En seguida presiona tu tecla Supr (Delete).
Otra forma es dar click en el punto que indica la existencia de la restricción desde la sección Layout:
También puedes presionar Click + Ctrl (Comand en MacOS) para eliminar la restricción directamente:
Posicionamiento Con Respecto Al Padre
Si deseas que él view se condicione a su padre, entonces usa el valor parent
en la restricción específica:
El ejemplo anterior muestra cómo alinear los bordes superior, izquierdo y derecho de un Button, con el borde superior del ConstraintLayout
que lo contiene. En XML esta definición sería:
<Button
android:id="@+id/button" ...
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Los valores posibles son:
layout_constraintLeft_toLeftOf
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
El primer lado pertenece al view de origen y el segundo al del view objetivo. Por lo que expresar app:layout_constraintEnd_toEndOf="parent"
se traduce a «el lado derecho de este botón se vincula al lado derecho del padre».
Nota: Los enunciados Start
y End
corresponden al lado izquierdo y derecho del view sin importar el tipo de lenguaje. Por lo que será de utilidad para cubrir configuraciones de idiomas que se escriben de derecha a izquierda.
Posicionamiento Con Respecto A Otro View
Para este caso la restricción toma el valor del atributo android:id
del view que se desea restringir con respecto a un lado. Esto facilita el ordenamiento en que son desplegados los elementos:
La imagen anterior muestra el uso de la restricción app:layout_constraintStart_toEndOf="@+id/button"
para posicionar el lado derecho de View 2 con el lado izquierdo de View 1:
<Button
android:id="@+id/button2" ...
app:layout_constraintStart_toEndOf="@+id/button" />
Alineación De Views
Supongamos que necesitamos alinear el lado superior de un view con el lado superior de otro. Para cumplir este cometido solo arrastra la restricción para que coincida con los lados de alineación:
O también el botón de alineación (Align) en la toolbar del ConstraintLayout
. Seleccionas los views que serán alineados y luego seleccionas el borde de alineación. En este ejemplo es Top Edges:
Estas mismas opciones las puedes encontrar en el menú contextual que se despliega al hacer click derecho en los views:
Usa los atributos que contienen al mismo lado tanto de origen como de objetivo, para alinear los bordes de los widgets. El ejemplo anterior es representado por la siguiente definición XML:
<Button
android:id="@+id/button2" ...
app:layout_constraintTop_toTopOf="@+id/button" />
Como es el borde superior, será Top_toTop
.
Alineación Por Línea Base Del Texto
La línea base del texto o baseline es la línea de referencia donde la mayoría de letras se sostienen y aquellos segmentos descendientes de letras como p, q o g, se extienden.
Si deseas alinear views por su baseline, entonces haz click derecho en el view y selecciona Show Baseline. Esto desplegará la línea de base y será apta para realizar conexiones con otras:
El atributo asociado para la restricción es layout_constraintBaseline_toBaselineOf
y al igual que las demás restricciones, recibe el id del view para alinear:
<Button
android:id="@+id/button2" ...
app:layout_constraintBaseline_toBaselineOf="@+id/button" />
Igualmente que las otras alineaciones, puedes alinear por líneas de base seleccionando Align>Baselines.
Agregar Márgenes
Especifica las márgenes de cada view para añadir espacio entre los views conectados a partir de restricciones:
Usa la ventana Attributes para editar los valores de las márgenes de un view a partir de:
- La escritura de un valor numérico mayor o igual a cero
- La selección de márgenes estándar en el menú desplegable
- La selección de un recurso de dimensiones con
@ ...
Asimismo usa el botón Default Margins en la toolbar para establecer el valor de las márgenes por defecto, cuando un view es agregado:
Los atributos asociados a las márgenes de cada lado son los siguientes:
android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
layout_marginBaseline
Por ejemplo, la margen de 136dp
a la derecha con respecto al padre mostrada anteriormente se implementa así:
<Button
android:id="@+id/button2" ...
android:layout_marginEnd="136dp"
android:layout_marginRight="136dp" />
Restricciones De Centrado Y Sesgo
Cuando aplicas las dos restricciones para un mismo eje, el ConstraintLayout centrará automáticamente el view con respecto a los bordes de destino:
El ejemplo anterior centra horizontalmente el view, por lo que su definición XML usa a :
<Button
android:id="@+id/button" ...
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
Ajustar Restricción De Sesgo
El bias o sesgo es una restricción que entra en acción cuando limitas ambos lados de un eje. Aunque en el centrado no se especifica el valor de este, su valor por defecto es 50% del espacio entre los orígenes de conexión.
Su objetivo es determinar la cantidad de desplazamiento que el view recorrerá en el eje acotado por restricciones.
Por ejemplo, si quisieras aplicar el 30% hacia la izquierda en vez del 50%, entonces puedes ir a la ventana Attributes y arrastrar el slider a dicho valor. O también arrastrar al view entre los muelles de acotación:
El sesgo es representado por los atributos layout_constraintHorizontal_bias
para el eje horizontal y layout_constraintVertical_bias
para el vertical. Ambos reciben un String con formato decimal para valores del rango [0.0, 1.0]:
<Button
android:id="@+id/button" ...
app:layout_constraintHorizontal_bias="0.3" />
Restricciones De Dimensiones
Al igual que en otros layouts, las dimensiones de los widgets dentro del ContraintLayout
son especificadas por los atributos android:width
y android:height
. Los siguientes son los posibles valores aceptados para modificar la forma en que el espacio es calculado.
Dimensión Específica
Aquí las dimensiones se representan por valores fijos en dp o por un recurso de dimensión. Por ejemplo, un ancho y alto de 50dp x 50dp:
También es posible hacer esta modificación desde la ventana Attributes si haces click en el control linear que se encuentra al interior del contorno:
WRAP_CONTENT
Este valor cumple el mismo cometido que en los otros layouts. Le permite al view ajustar su tamaño según las dimensiones del contenido.
Por ejemplo, si un TextView tiene el String «ABC» su tamaño será menor que si tiene «ABC DEF», ya que calcula su ancho basado en el contenido:
Aplica este valor desde Attributes modificando el controlador con el siguiente icono:
MATCH_CONSTRAINT
Con este valor las dimensiones del view son expandidas hasta ocupar todo el espacio restante que sus restricciones le posibilitan.
Por ejemplo, si quisiéramos que el TextView ocupe todo el espacio horizontal sin importar su contenido:
Desde el editor aplicas este valor llevando la línea de dimensión al siguiente estado:
En el formato XML verás que MATCH_CONSTRAINT
es equivalente a usar el valor 0dp
en la dimensión:
<TextView
android:id="@+id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content".../>
Ratio
Si deseas establecer las dimensiones de un view a partir de la relación de aspecto (la razón entre su ancho y alto) usa el atributo app:layout_constraintDimensionRatio
. O presiona la esquina superior izquierda Toggle Aspecto Ratio Constraint para revelar un campo de texto para especificar el ratio.
Esta restricción ocupará todo el espacio disponible que las restricciones le permitan y además mantendrá la relación de aspecto.
Su uso estará disponible cuando alguna dimensión tiene MATCH_CONSTRAINT
. Los valores pueden ser números flotantes o relaciones ancho:alto
(siendo 1:1
el valor inicial). Por ejemplo, un ImageView
con un aspecto de proporción vertical 16:9:
En el código verás el atributo app:layout_constraintDimensionRation
con el valor del par de relación acompañado de una
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="wrap_content"...
app:layout_constraintDimensionRatio="w,16:9" />
Si quieres restringir una de las dos dimensiones usa las letras w
para el ancho o h
para el alto, separado por una coma con el aspecto.
Aplicando Restricciones
Teniendo en cuenta el conocimiento previo, pasemos a mejorar el layout de ejemplo que trabajamos.
Estos es:
- Aplicar márgenes de 16 dp para todas las restricciones, excepto el vínculo del autor con la fecha de publicación
- Usar todo el espacio disponible para los tamaños de la imagen destacada, el título y el contenido del post
- Poner el sesgo horizontal del título del post en 0.0
- Usar una aspecto de relación 16:9 para a imagen destacada y fijar su altura en 232dp
La definición XML final sería:
<?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">
<ImageView
android:id="@+id/post_featured_image"
android:layout_width="0dp"
android:layout_height="232dp"
android:contentDescription="@string/featured_image_desc"
android:src="@drawable/featured_image"
app:layout_constraintDimensionRatio="w,16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/post_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
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:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:text="James Revelo"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/post_category"
app:layout_constraintHorizontal_bias="0.0"
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_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:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
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="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
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:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginBottom="16dp"
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:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:text="Siguiente"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
En la previa podrás observar la materialización de todas las restricciones existentes:
Cadenas En Un ConstraintLayout
En este tutorial aprendiste el uso de las restricciones del ConstraintLayout
para establecer posiciones, márgenes, centrados, sesgos y dimensiones.
Lo siguiente será abordar el uso de cadenas. Estas son restricciones que vinculan grupos de views, con el fin de restringir su relación bidireccional (horizontal o vertical) y aplicar distribución de espacios.
Más Contenidos Android
- Tutoriales de interfaz de usuario en Android (todo)
- Guía de desarrollo Android
- Cursos de desarrollo Android
Ú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!