Uso de ConstraintLayout

Este nuevo layout ha sido añadido en una librería de compatibilidad, por lo que se nos anima a usarlo de forma predeterminada. Nos permite crear complejos diseños sin la necesidad de usar layout anidados. El hecho de realizar diseños donde un layout se introduce dentro de otro y así repetidas veces, ocasionaba problemas de memoria y eficiencia en dispositivos de pocas prestaciones.
Es muy parecido a RelativeLayout, pero más flexible y fácil de usar desde el editor visual de Android Studio (disponible desde la versión 2.3). De hecho, se recomienda crear tu layout con las herramientas drag-and-drop, en lugar de editar el fichero XML. El resto de layouts, son más fáciles de crear desde XML.
Las posiciones de las diferentes vistas dentro de este layout se definen usando constraint (en castellano restricciones). Un constraint puede definirse en relación al contenedor (parent), a otra vista o respecto a una línea de guía (guideline). Es necesario definir para cada vista al menos un constraint horizontal y uno vertical. Pero también podemos definir más de un constraint en el mismo eje. Veamos un ejemplo:

Observa como la vista A está posicionada con respecto al contenedor. La vista B está posicionada verticalmente con respecto a la vista A, aunque se ha definido un segundo constraint con respecto al lado derecho del contenedor. Veremos más adelante cómo se maneja esta circunstancia.
También podemos posicionar las vistas usando alineamientos. Observa como el borde superior de B está alineado con el borde superior de A. La vista C define dos alineamientos horizontales simultáneos pero niguno vertical, lo que ocasionará un error de compilación. También podemos realizar alineamientos usando la línea base de texto y líneas de guía, como se muestra a continuación:
Ejercicio: Creación de un layout con ConstraintLayout

1.     Abre el proyectoPrimerasVistas o crea uno nuevo.

2.     En Gradle Scripts/Bulid.gradle (Module:app) ha de estar la dependencia:

dependencies {
    …
    implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
}

3.     Abre el layout activity_main.xml creado en modo diseño vsual, para ello ha de estar seleccionada la pestaña Design de la esquina superior derecha.

4.     Selecciona la vista de tipo
TestView con texto “Hello Word!” y pulsa el botón <Supr> para eliminarla.

5.     Si es necesario, desactiva la opción de Autoconnect de la barra de acciones del ConstraintLayou. Es el segundo icono con forma de imán:

Tener activa esta opción es útil para diseñar más rápido los layouts. No obstante, a la hora de aprender a usar los constraint, es mejor ir haciéndolos de uno en uno.

6.    Dentro del área Palette, selecciona Commom y arrastra una vista de tipo ImageView al área de diseño. Se abrirá una ventana con diferentes recursos Drawable. Selecciona la pestaña Mip Map y,  ic_launcher.

7.    Para definir el primer constraint, pulsa sobre el punto de anclaje que aparece en la parte superior del ImageView y arrástralo hasta el borde superior del contenedor:

8.    En la parte derecha, en la sección Layout, nos aparece un editor visual para los constraint. Por defecto la distancia seleccionada ha sido 8dp. Pulsa sobre este número y cámbialo a 32dp:

 

NOTA:Según las recomendaciones de Material Design los márgenes y tamaños han de ser un múltiplo de 8dp.

9.     Realiza la misma operación con el punto de anclaje izquierdo, arrastrándolo al borde izquierdo. Ya tenemos la restricción horizontal y vertical por lo que la vista está perfectamente ubicada en el layout.

10.     Arrastra el punto de anclaje derecho al borde derecho. Introduciendo una distancia de 32dp a izquierda y derecha:

Observa como en este caso, al tener que cumplir simultáneamente dos constraint horizontales la imagen es centrada horizontalmente. Esto se representa con la línea en zigzag, representando un muelle, que estira de la vista desde los dos lados.

11.     Si en lugar de querer la imagen centrada la queremos en otra posición, podemos ir al editor de constraint y usar la barra deslizante (Horizontal Bias). Desplázala a la posición 25.

con

Observa en el área de trabajo, como la longitud del muelle de la izquierda es un 25%, frente al 75% del muelle de la derecha.

12.     Seleccionando el ImageView en el área de trabajo, arrastra el cuadrado azul de la esquina inferior derecha, hasta aumentar su tamaño hasta 96x96 dp (múltiplos de 8). Otra alternativa es modificar los valores layout_width y layout_height en el área Properties.

13.     Desde lel marco  Palette /Common, añade un TextView a la derecha del ImageView. Arrastra el punto de anclaje de la izquierda hasta el punto de la derecha de la imagen y establece un margen de 64 dp. Arrastra el punto de anclaje superior del TextView hasta el punto superior de la imagen:
 

14.     Añade un nuevo TextView bajo el anterior, con texto “hola”. Introduce tres constraint, usando los puntos de anclaje inferior, izquierdo y derecho, tal y como se muestra en la siguiente figura: 
 
El margen inferior ha de ser 16dp y el izquierdo y derecho 0. De esta forma hemos centrado horizontalmente los dos TextView.

15    También podemos conseguir que el ancho del nuevo TextView coincida con el superior. Para ello selecciona la vista y en el campo layout_width e introduce mach_constraint ó  0dp. Con esto, hacemos que el ancho se calcule según las restricciones de los constraint.
 

16.     Haz que desde MainActivity se visualice este layout y ejecuta el proyecto en un dispositivo.
Una vez familiarizados con los conceptos básicos de los constraint vamos a ver con más detalle las herramientas disponibles. Veamos en editor de constraint:

(1) relación de tamaño: Puede establecer el tamaño de la vista en una proporción, por ejemplo 16:9, si al menos una de las dimensiones de la vista está configurada como "ajustar a constraint" (0dp). Para activar la relación de tamaño, haz clic donde señala el número 1.
(2) eliminar constraint: Se elimina la restricción para este punto de anclaje.
(3) establecer alto/ancho: Para cambiar la forma en la que se calcula las dimensiones de la vista, pulsa en este elemento. Existen tres posibilidades:
ajustar a contenido: equivale al valor
warp_content. (Ej. 1er
TextView)
 ajustar a constraint: equivale a poner 0dp. (Ej. 2º
TextView)
tamaño fijo: equivale a poner un valor concreto de dp. (Ej.
ImageView)

Aunque se representan 4 segmentos, realmente podemos cambiar 2, los horizontales para el ancho y los verticales al alto.
(4) establecer margen: Podemos cambiar los márgenes de la vista.
(5)  Sesgo del constraint: Ajustamos como se reparte la dimensión sobrante.


También es importante repasar las acciones disponibles cuando trabajamos con ConstraintLayout:

  Ocultar constraint: Elimina las marcas que muestra las restricciones y los márgenes existentes.
 Autoconectar: Al añadir una nueva vista se establecen unos constraint con elementos cercanos de forma automática.
Definir márgenes: Se configura los márgenes por defecto.

  Borrar todos los constraint: Se eliminan todas las restricciones del layout.
 Crear automáticamente constraint: Dada una vista seleccionada, se establecen unos constraint con elementos cercanos de forma automática.
 Empaquetar / expandir: Se agrupan o se separan los elementos.
Alinear: Centra o justifica los elementos seleccionados.
Añadir línea de guía: Se crea una nueva línea de referencia.

Uso de escritura derecha a izquierda (avanzado)

Android permite adaptar los layouts a la escritura derecha-izquierda usada en árabe o hebreo. Cuando un dispositivo esté configurado de esta forma, también los formularios se mostrarán de derecha a izquierda. El siguiente ejemplo muestra un formulario y como nos interesaría que se viera en un dispositivo configurado en árabe.

Para conseguir este efecto, en lugar de utilizar los atributos tradicionales basados en Left y Right, usaremos atributos basados en Start y End. Veamos un ejemplo

<androidx.constraintlayout.widget.ConstraintLayout …>
   <ImageView android:id="@+id/imageView“ …
       android:layout_marginStart="32dp"
       app:layout_constraintStart_toStartOf="parent"/>
   <TextView …
       app:layout_constraintStart_toEndOf="@+id/imageView“
       app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Dependiendo de la configuración del dispositivo se mostrará de la siguiente forma:

Como puedes observar, en el primer caso Start y End, significan Left y Right. Y en el segundo, significan Right y Left.
Si quieres que el layout se vea igual en ambos casos, utiliza atributos basados en Left y Right.

<androidx.constraintlayout.widget.ConstraintLayout …>
   <ImageView android:id="@+id/imageView“ …
       android:layout_marginLeft="32dp"
       app:layout_constraintLeft_toLeftOf="parent"/>
   <TextView …
       app:layout_constraintLeft_toRightOf="@+id/imageView"
       app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

El resultado será:

Posicionamiento Circular  (avanzado)

Podemos situar una vista respecto a otra usando coordenadas polares. Para ello tendremos que indicar tres atributos: la vista de donde partimos, el radio (o distancia entre los centros de ambas vistas) y el ángulo (en grados de 0° en la parte superior a 360°). En el ejemplo la vista B se sitúa respecto a la A, con una distancia de
100dp y 45°.

<ImageView
    …
    android:id="@+id/B"
    app:layout_constraintCircle="@+id/A"
    app:layout_constraintCircleAngle=“45"
    app:layout_constraintCircleRadius="100dp"/>  <

Ejercicio: Líneas guía y cadenas en ConstraintLayout

1.     Siguiendo con el Layout del ejercicio anterior. Pulsa en la acción Guideline , y selecciona Add Horizontal GuideLine. Aparecerá un circulo gris con un pequeño triángulo pegado al borde izquierdo, desde donde sale una línea guía horizontal. Arrástrala hacia abajo, hasta separarla una distancia de 160dp.

2.     Esta guía nos permite dividir el layout en dos áreas, la superior donde ya hemos realizado una especie de cabecera y la inferior. A partir de ahora los elementos de la parte inferior los colocaremos en relación a esta línea guía.
3.     Selecciona el
ImageView, cópialo (Ctrl+C) y pégalo tres veces (Ctrl+V). Acabamos de hacer tres copias de la imagen, pero no son visibles al estar en la misma posición. En el área Component Tree, selecciona imageView2 y arrastrarlo bajo la línea guía, pegado a la izquierda. Coloca imageView3 bajo la línea guía, en el centro e imageView4, a la derecha de este
4.     Selecciona 
imageView2, con el botón derecho y borra todos sus constraint seleccionando Clear Constraint of Selección. Repite esta operación para imageView3 y imageView4.
5.     Las tres imágenes han de tener un constraint desde el punto de anclaje superior a la línea guía con un margen de 32dp. Desde el punto de anclaje izquierdo. En las otras dos imágenes, del izquierdo al punto de anclaje derecho de la vista de su izquierda.
imageView2, establece un constraint El punto de anclaje derecho de la vista de la derecha. El punto de anclaje derecho de la tercera vista únelo al borde derecho. El resultado ha de ser el siguiente:

6.     Para conseguir que estas tres vistas formen una cadena, selecciónalas y utiliza la acción Align  / Horizontaly o el botón derecho Chains / Create Horizontal Chain:

Observa cómo las vistas ahora están unidas por medio de un conector de cadena.
Si abres la lengüeta Code, para estudiar el XML, puedes comprobar que para establecer la cadena se han añadido dos constraint, desde el punto de anclaje derecho de las dos primeras vista hacia la vista de su izquierda. Es decir, una restricción mutua, de A -> B y de B -> A.

Nota: En la versión actual del editor visual, parece que establecer la cadena indicando estos constraint individualmente no parece posible. Hay que usar la acción Align   / Center Horizontaly o Chains/Create Horizontal Chain.  

7.     También es posible otras distribuciones de cadena. Podemos hacer que los márgenes se distribuyan solo entre las vistas o solo en los extremos izquierdo y derecho:  

Si abres la lengüeta Code puedes comprobar que estas dos nuevas configuraciones de cadena se consiguen añadiendo en la primera vista el atributo:
 

 app:layout_constraintHorizontal_chainStyle="spread_inside"
para la primera distribución y para la segunda:
 
  app:layout_constraintHorizontal_chainStyle="packed"
Para la distribución del punto anterior el valor es "spread" o no indicar nada.

8.     Existe otra distribución en la que los márgenes desaparecen y se ajusta el ancho de las vistas hasta cubrir todo el espacio disponible. Selecciona la vista central y en el editor de constraint pulsa sobre el icono     hasta que aparezca   . Recuerda que esta acción es equivalente a poner 0 en layout_width. El resultado se muestra a la izquierda: 

Para conseguir el resultado de la derecha, hemos repetido la operación con las otras dos imágenes.
Si en lugar de repartir los anchos por igual, quieres otra configuración, puedes usar el atributo
layout_constraintHorizontal_weight (funciona igual que layout_weight en un LinearLayout)

9.     Ejecuta el proyecto en un dispositivo.
Al crear constraint, recuerde las siguientes reglas:

  • Cada vista debe tener al menos dos restricciones: una horizontal y otra vertical.
  • Sólo puede crear restricciones que comparten el mismo plano. Así, el plano vertical (los lados izquierdo y derecho) de una vista puede anclarse sólo a otro plano vertical.
  • De cada punto de restricción (superior, inferior, derecha o izquierda) solo puede salir una flecha. Pero pueden llegar varias flechas desde diferentes vistas.

Práctica: Uso de layouts
1.   
Continuando con el ejercicio anterior, crea un nuevo layout. Para ello, pulsa con el botón derecho sobre app/res/layout y selecciona New/Layout resource file. Como nombre introduce “entrada_texto” y en Root element:
androidx.constraintlayout.widget.ConstraintLayout.

2.     Añade una línea guía horizontal. Pulsa sobre el icono en forma de triangulo que aparece a la izquierda de la línea guía, hará que aparezca el símbolo de %. Desplaza la línea hasta que se sitúe en el 50%

3.    Añade de la paleta Text una vista de tipo Plain Text. Trata de centrar esta vista en la parte mitad superior del layout. Añade un TexView y dos Button y trata de situarlos como se muestra a continuación. Modifica los textos y añade una separación de 8dp entre las vistas.

4.     Verifica que el layout se muestra correctamente en horizontal y en vertical, usando la opción del menú.

5.     Modifica
MainActivity para que se visualice este layout en la actividad. Ejecuta la aplicación y verifica el resultado.

6.  En la parte inferior añade tres
TexView con los textos mostrados abajo. Modifica la propiedad textSize para que valga 30sp. Haz que el TexView “primero” esté centrado horizontalmente. Los otros dos TexView se alinearán horizontalmente ajustándolos a la derecha de “primero”. Crea una cadena para distribuir las tres vistas verticalmente en la mitad inferior del layout. La separación entre ellas, con la línea guía y con la parte inferior ha de ser la misma.

NOTA: Si con las herramientas visuales no consigues que la cadena comience en la línea guía, utiliza el editor de código para conseguirlo.

7. Ejecuta la aplicación y verifica el resultado.
 

Práctica: Uso de layouts

1. Continuando con el ejercicio anterior, crea un nuevo layout . Como nombre introduce “calculadora” y en Root element:
androidx.constraintlayout.widget.ConstraintLayout.

2. Realizar un diseño similar al siguiente. Ha de ocupar todo el ancho y alto disponible. Ha de visualizarse correctamente en vertical y en horizontal. Separación entre botones de 10dp.
 

Modifica MainActivity para que se visualice este layout. Ejecuta la aplicación y verifica el resultado.

 

video[Tutorial]  Uso de ConstraintLayout en Android
video[Tutorial]  ConstraintLayout: Cadenas, Líneas Guía y Posicionamiento circular

Preguntas de repaso: ConstraintLayout