Los objetos virtuales del ConstraintLayout son elementos especiales que representan referencias auxiliares sobre las cuales puedes aplicar restricciones.
Estos no son renderizados en el dispositivo, ya que son usados sólo para propósitos de diseño en tu layout.
Por lo que en este tutorial aprenderás sobre:
- Líneas de guía
- Barreras
- Grupos
Nota: Esta es la cuarta parte de la guía de ConstraintLayout (todo), por lo que te recomiendo leer las anteriores para aprender sobre los fundamentos de este componente.
Ejemplo De Objetos Virtuales Del ConstraintLayout
Los siguientes son tres diseños simples que tomaremos para ver el uso de los objetos de ayuda sobre nuestros layouts:
Puedes descargar el proyecto Android Studio con los archivos XML desde el siguiente botón:
Líneas De Guía (Guidelines)
Usa guías de línea, representadas por la clase Guideline
, sirven para trazar una referencia lineal horizontal o vertical, con el fin de aplicar restricciones a tus widgets. Este objeto se extiende por todo el tamaño del ConstraintLayout
y se posiciona según la medida que especifiques.
Crear Guideline Desde El Editor De Layouts
Para ver su utilidad, crea un layout llamado p4_guidelines.xml
y añade cuatro TextViews.
Existen varias formas de añadir una guideline. La primera de ellas es ir a Pallete>Common>Helpers y arrastrar el componente Guideline (orientación):
La segunda consiste en dar click derecho en el lienzo o el Component Tree y seleccionar Add Helpers>[Orientación] Guideline:
Y la tercera es hacer click en el botón Guidelines desde la toolbar del editor de layouts:
Para ver sus atributos, selecciona la guideline y ve a la ventana Attributes. Aunque aparecen varios elementos para restricciones, modificarlos no tendrá efecto en su contenido:
Con esto en mente, añade una línea de guía vertical y otra horizontal. Tu definición XML se verá similar a esta:
<?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">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="68dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/horizontal_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="342dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
La etiqueta <Guideline>
usará el atributo android:orientation
para determinar la orientación de la línea de guía y app:layout_constraintGuide_*
para su forma de posicionamiento.
Posicionar Guidelines
Desplaza las líneas de guía haciendo click en los extremos de la línea punteada y arrastrándola sobre el eje:
Hay tres formas distintas de establecer la posición:
layout_constraintGuide_begin
: Se cuenta la distancia desde el lado izquierdo (horizontal) o superior (vertical)layout_constraintGuide_end
: Se cuenta la distancia desde el lado derecho (horizontal) o inferior (vertical)layout_constraintGuide_percent
: La posición se establece según un porcentaje del ancho o alto del layout
Cambia entre estos valores haciendo click sobre el indicador de la línea de guía:
En nuestro ejemplo usaremos el posicionamiento por porcentaje, ya que deseamos trazar las líneas por el centro del ConstraintLayout
sin importar el tamaño de la pantalla:
Conectar Views Con Guidelines
La aplicación de restricciones a los views es exactamente igual. Solo arrastra los conectores y marca la línea de guía como destino.
En nuestro ejemplo deseamos marcar los cuadrantes del plano cartesiano a partir del centro, por lo que aplicamos restricciones de posición sobre cada guideline para separar las 4 secciones:
Las restricciones de los TextViews y el posicionamiento de las guidelines se verían así:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/quadrant1"
android:layout_width="wrap_content"
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:layout_marginBottom="16dp"
android:text="I"
android:textSize="60sp"
app:layout_constraintBottom_toTopOf="@+id/horizontal_guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/vertical_guideline"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/quadrant2"
android:layout_width="wrap_content"
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:layout_marginBottom="16dp"
android:text="II"
android:textSize="60sp"
app:layout_constraintBottom_toTopOf="@+id/horizontal_guideline"
app:layout_constraintEnd_toStartOf="@+id/vertical_guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/quadrant3"
android:layout_width="wrap_content"
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:layout_marginBottom="16dp"
android:text="III"
android:textSize="60sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/vertical_guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/horizontal_guideline" />
<TextView
android:id="@+id/quadrant4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:text="IV"
android:textSize="60sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/vertical_guideline"
app:layout_constraintTop_toTopOf="@+id/horizontal_guideline" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/horizontal_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
Barreras (Barriers)
Una barrera (Barrier
) toma como referencia a múltiples views y crea una línea de guía desde el elemento que ocupe más espacio con respecto a un lado.
En la imagen anterior hay dos views y se ha tomado el lado derecho como destino. La barrera es posicionada al extremo derecho del view 1 porque es más ancho.
Si el tamaño de ambos elementos cambia, entonces la barrera se adaptará para avanzar o replegarse según las dimensiones del elemento más extenso. Esto se ve en la segunda imagen donde la barrera se ajusta al view 2 cuando es ampliado.
Crear Barrera Desde El Editor De Layouts
Supongamos que deseamos aplicar una barrera en tres características del detalle de un producto: Nombre, Vendedor y Colores disponibles.
Crea un layout llamado p4_barriers.xml
y añade seis TextViews para las etiquetas de las características y el valor.
Al igual que las líneas de guía, las barreras pueden ser añadidas desde la toolbar del editor, la ventana Pallete y con el menú contextual al hacer click derecho en el lienzo.
Como necesitamos restringir las etiquetas por su lado derecho, añadiremos una barrera vertical al layout:
Al crearla la podrás ver en la jerarquía:
Añadir Views A La Barrera
Luego sitúate en la ventana Component Tree y arrastra los widgets que deseas hagan parte de la barrera:
Android Studio creará la barrera en el borde por defecto del eje. En este caso, como es una barrera horizontal, será alineado a la izquierda:
Los views que pertenecen a la barrera se añaden en una lista separada por comas desde el atributo app:constraint_referenced_ids
:
<androidx.constraintlayout.widget.Barrier
android:id="@+id/labels_barrier"...
app:constraint_referenced_ids="colors_label,seller_label,name_label" />
Determinar Dirección De La Barrera
La dirección anterior no es la apropiada. Para cambiarla, establece el valor del atributo barrierDirection
en la ventana de atributos. Para nuestro ejemplo será el valor end
:
De esta forma, las etiquetas serán respaldadas por la barrera en la parte derecha:
Conectar Views Con La Barrera
Con la línea punteada definida por la barrera, ahora los textos correspondientes a las etiquetas pueden ser restringidos. Al igual que con las guidelines, solo arrastra líneas de conexión:
El equivalente desde XML es asignar el id de la barrera a la restricción de posicionamiento relativo. Por ejemplo, los atributos app:layout_constraintStart_toEndOf
de los textos del producto toman a labels_barrier
:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="32dp"
android:text="Nombre"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/seller_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:text="Vendedor"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/name_label" />
<TextView
android:id="@+id/colors_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:text="Colores disponibles"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seller_label" />
<TextView
android:id="@+id/name_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:text="Camisa Ask"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
app:layout_constraintBottom_toBottomOf="@+id/name_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/labels_barrier"
app:layout_constraintTop_toTopOf="@+id/name_label" />
<TextView
android:id="@+id/seller_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:text="Moda Extrema"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
app:layout_constraintBottom_toBottomOf="@+id/seller_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/labels_barrier"
app:layout_constraintTop_toTopOf="@+id/seller_label" />
<TextView
android:id="@+id/colors_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:text="Azul, Verde y Blanco"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
app:layout_constraintBottom_toBottomOf="@+id/colors_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/labels_barrier"
app:layout_constraintTop_toTopOf="@+id/colors_label" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/labels_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="colors_label,seller_label,name_label" />
</androidx.constraintlayout.widget.ConstraintLayout>
Grupos (Groups)
Por otro lado, los grupos son objetos de ayuda representados por la clase Group
, cuyo objetivo es controlar la visibilidad y elevación de múltiples widgets.
En el caso de que un view pertenezca a varios grupos, su visibilidad será definida por aquel grupo que haya sido declarado de último en la definición XML.
Crear Grupos
Supongamos que tenemos un botón que inicia un cálculo hipotético que toma varios segundos. Y mientras se ejecuta se debe mostrar una barra de progreso junto a un texto que decore la intención.
Para este diseño usaremos un grupo que incluya la ProgressBar
y el TextView
del estado de carga. De esta forma podremos modificar la visibilidad de ambos, al referirnos al grupo.
Por lo tanto crea un layout llamado p4_grupos.xml
y añade los componentes nombrados. De la misma forma que para los objetos anteriores, añade un grupo desde el editor:
Añadir Views A Un Grupo
Si abres el Component Tree, verás el grupo como parte de la jerarquía. Y como con las barreras, añade los elementos arrastrándolos hacia el:
Esto desde XML, equivale a ir hacia la etiqueta <Group>
y modificar el atributo app:constraint_referenced_ids
a fin de asignarle una lista con los ids de los miembros.
<androidx.constraintlayout.widget.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:visibility="gone"
android:layout_height="wrap_content"
app:constraint_referenced_ids="progress,empty_text" />
Debido a que la barra de progreso y el texto de carga están escondidos inicialmente, entonces asigna gone
al atributo android:visibility
.
Modificar Visibilidad Del Grupo
Por último, ve a MainActivity
y toma la referencia del botón del cálculo y el grupo:
class MainActivity : AppCompatActivity() {
private lateinit var calculate: Button
private lateinit var loadingGroup: Group
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.p4_groups)
calculate = findViewById(R.id.calculate)
loadingGroup = findViewById(R.id.group)
calculate.setOnClickListener {
calculate()
}
}
private fun calculate() {
}
}
El método calculate()
iniciaremos una corrutina y aplicaremos el método delay()
para un retraso de 3 segundos. Al mismo tiempo, cambiaremos la visibilidad del grupo hacia View.VISIBLE
y la del botón a View.GONE
:
private fun calculate() {
showLoadingState(true)
MainScope().launch {
delay(3000)
showLoadingState(false)
}
}
private fun showLoadingState(show: Boolean) {
loadingGroup.visibility = if (show) View.VISIBLE else View.GONE
calculate.visibility = if (show) View.GONE else View.VISIBLE
}
Claramente, una vez se termine la tarea, retornaremos las visibilidades a sus valores iniciales con una expresión if.
Cómo ves, el uso de grupos evita que crees un ViewGroup
para aplicar visibilidad a múltiples elementos.
Animaciones En El ConstraintLayout
En este tutorial viste el uso de tres de los objetos virtuales más populares con el ConstraintLayout
.
Aprendiste que las líneas de guía permiten restringir elementos a partir de trazos posicionados con respecto al layout. Que las barreras trazan límites lineales para múltiples views y que los grupos permiten manejar visibilidades de varios elementos como una unidad.
Vamos a finalizar esta guía viendo cómo construir animaciones a partir del uso de la clase ConstraintSet
y el manejador TransitionManager
.
Más Contenidos Android
- Cadenas en el ConstraintLayout
- Tutoriales de interfaz gráfica en Android (todo)
- Cursos de desarrollo Android