Si queremos combinar varios elementos de tipo vista, tendremos que utilizar un objeto de tipo layout. Un layout es un contenedor de una o más vistas y controla su comportamiento y posición. Hay que destacar que un layout puede contener otro layout y que es un descendiente de la clase View.
La siguiente lista describe los layout más utilizados en Android:
LinearLayout: Dispone los elementos en una fila o en una columna.
TableLayout: Distribuye los elementos de forma tabular.
RelativeLayout: Dispone los elementos con relación a otro o al padre.
ConstraintLayout: Versión mejorada de RelativeLayout, que permite una edición visual desde el editor.
FrameLayout: Permite el cambio dinámico de los elementos que contiene.
AbsoluteLayout: Posiciona los elementos de forma absoluta.
Dado que un ejemplo vale más que mil palabras, pasemos a mostrar cada uno de estos layouts en acción:
LinearLayout es uno de los layout más utilizado en la práctica. Distribuye los elementos uno a continuación de otro, bien de forma horizontal o vertical.
<LinearLayout xmlns:android="http://...
android:layout_height="match_parent"
android:layout_width="match_parent"
android: orientation ="vertical">
<AnalogClock
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un checkBox"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un botón"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un texto cualquiera"/>
</LinearLayout>
TableLayout distribuye los elementos de forma tabular. Se utiliza la etiqueta <TableRow> cada vez que queremos insertar una nueva línea.
<TableLayout xmlns:android=”http://...
android:layout_height="match_parent"
android:layout_width="match_parent">
<TableRow>
<AnalogClock
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un checkBox"/>
</TableRow>
<TableRow>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un botón"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un texto cualquiera"/>
</TableRow>
</TableLayout>
RelativeLayout permite comenzar a situar los elementos en cualquiera de los cuatro lados del contenedor e ir añadiendo nuevos elementos pegados a estos.
<RelativeLayout
xmlns:android="http://schemas...
android:layout_height="match_parent"
android:layout_width="match_parent">
<AnalogClock
android:id="@+id/AnalogClock01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"/>
<CheckBox
android:id="@+id/CheckBox01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below = "@id/AnalogClock01"
android:text="Un checkBox"/>
<Button
android:id="@+id/Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un botón"
android:layout_below = "@id/CheckBox01"/>
<TextView
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom = "true"
android:text="Un texto cualquiera"/>
</RelativeLayout>
ConstraintLayout versión más flexible y eficiente de RelativeLayout.
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas...
android:layout_height="match_parent"
android:layout_width="match_parent">
<AnalogClock
android:id="@+id/AnalogClock01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf= "parent"
app:layout_constraintTop_toTopOf= "parent"/>
<CheckBox
android:id="@+id/CheckBox01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un checkBox"
app:layout_constraintTop_toBottomOf=
"@+id/AnalogClock01"
app:layout_constraintTop_toTopOf= "parent"
<Button
android:id="@+id/Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un botón"
app:layout_constraintTop_toBottomOf=
"@+id/CheckBox01"
app:layout_constraintLeft_toLeftOf=
"@+id/CheckBox01"/>
<TextView
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un texto cualquiera"
app:layout_constraintBottom_toBottomOf= "parent"
app:layout_constraintLeft_toLeftOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
FrameLayout posiciona las vistas usando todo el contenedor, sin distribuirlas espacialmente. Este layout suele utilizarse cuando queremos que varias vistas ocupen un mismo lugar. Podemos hacer que solo una sea visible, o superponerlas. Para modificar la visibilidad de un elemento utilizaremos la propiedad visibility.
<FrameLayout xmlns:android="http://schemas...
android:layout_height="match_paren"
android:layout_width="match_parent">
<AnalogClock
android:layout_width="wrap_content"
android:layout_height="wrap_conten"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un checkBox"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un botón"
android:visibility="invisible"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un texto cualquiera"
android:visibility="invisible"/>
</FrameLayout>
AbsoluteLayout permite indicar las coordenadas (x, y) donde queremos que se visualice cada elemento. No es recomendable utilizar este tipo de layout. La aplicación que estamos diseñando tiene que visualizarse correctamente en dispositivos con cualquier tamaño de pantalla. Para conseguirlo, no es una buena idea trabajar con coordenadas absolutas. De hecho, este tipo de layout ha sido marcado como obsoleto.
<AbsoluteLayout xmlns:android="http://schemas.
android:layout_height="match_parent"
android:layout_width="match_parent">
<AnalogClock
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x= "50px"
android:layout_y="50px"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un checkBox"
android:layout_x= "150px"
android:layout_y="50px"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un botón"
android:layout_x= "50px"
android:layout_y="250px"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Un texto cualquiera"
android:layout_x= "150px"
android:layout_y="200px"/>
</AbsoluteLayout>
Si tienes dudas sobre cuándo emplear cada layout, usa la siguiente tabla:
LinearLayout: Diseños muy sencillos.
RelativeLayout: Nunca, hay una nueva alternativa.
ConstraintLayout: Usar por defecto.
FrameLayout: Varias vistas superpuestas.
AbsoluteLayout: Nunca. Aunque está bien conocerlo por si acaso.
Vídeo[tutorial]: Los layouts en Android
Preguntas de repaso: Tipos de layouts
Preguntas de repaso: Atributos de los layouts
También podemos utilizar otros layouts, que se describen a continuación:
ScrollView: Visualiza una vista en su interior; cuando esta no cabe en pantalla, se permite un deslizamiento vertical.
HorizontalScrollView: Visualiza una vista en su interior; cuando esta no cabe en pantalla, se permite un deslizamiento horizontal.
TabLayout, FragmentTabHost, TabLayout o TabHost: Proporciona una lista de pestañas que pueden ser pulsadas por el usuario para seleccionar el contenido a visualizar. Se estudia al final del capítulo.
ListView: Visualiza una lista deslizable verticalmente de varios elementos. Su utilización es algo compleja. Se verá un ejemplo en el capítulo siguiente.
GridView: Visualiza una cuadrícula deslizable de varias filas y varias columnas.
RecyclerView: Versión actualizada que realiza las mismas funciones que ListView o GridView. Se verá en el siguiente capítulo.
ViewPager: Permite visualizar una serie de páginas, donde el usuario puede navegar arrastrando a derecha o izquierda. Cada página ha de ser almacenada en un fragment.
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 layouts 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. No obstante, también podemos definir más de un constraint en el mismo eje. Veamos un ejemplo:
Observa cómo 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 cómo el borde superior de B está alineado con el borde superior de A. La vista C define dos alineamientos horizontales simultáneos, pero ninguno 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 proyecto PrimerasVistas o crea uno nuevo.
2. En Gradle Scripts/Bulid.gradle (Module:app) ha de estar la dependencia:
3. Abre el layout activity_main.xml creado en el ejercicio anterior. Pulsa con el botón derecho en Component Tree y selecciona la opción Convert LinearLayout to ConstraintLayout. Esta herramienta nos permite convertir nuestros viejos diseños que se basaban en LinearLayout o RelativeLayout en este nuevo tipo de layouts. Por desgracia, no siempre funciona todo lo bien que desearíamos.
4. 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 “constraint” y en Root element: androidx.constraintlayout.widget.ConstraintLayout.
5. Vamos a desactivar 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 Common y arrastra una vista de tipo ImageView al área de diseño. Se abrirá una ventana con diferentes recursos Drawable. Selecciona en Project, 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 superior derecha 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:
Observa como en este caso, al tener que cumplir simultáneamente dos constraint horizontales la imagen se centra horizontalmente. Esto se representa con la línea en zigzag, representando un muelle, que estira de la vista desde los dos lados. El pequeño botón con una cruz roja que aparece nos permite borrar todos los constraint de la vista.
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ázarla a la posición 25.
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 a 96x96 dp (múltiplos de 8). Otra alternativa es modificar los valores layout_width y layout_height en el área Properties.
13. Desde el 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 match_constraint o 0dp. Con esto, hacemos que el ancho se calcule según las restricciones de los constraint. Quita los márgenes laterales para que los anchos de las dos vistas coincidan.
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 a 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 para el alto.
(4) establecer margen: Podemos cambiar los márgenes de la vista.
(5) sesgo del constraint: Ajustamos cómo se reparte la dimensión sobrante.
También es importante repasar las acciones disponibles cuando trabajamos con ConstraintLayout:
Ocultar constraint: Elimina las marcas que muestran 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.
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 círculo 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 arrástralo 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 e 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 de imageView2, establece un constraint con el borde izquierdo y en las otras dos imágenes, al 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 Text 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 / 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 Text 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.
• Solo puede crear restricciones que compartan el mismo plano. Así, el plano vertical (los lados izquierdo y derecho) de una vista puede anclarse solo a otro plano vertical.
• Cada manejador de restricción puede utilizarse para una sola restricción, pero puede crear varias restricciones (desde vistas diferentes) al mismo punto de anclaje.
Práctica: Uso de layouts
1. Utiliza un ConstraintLayout para realizar un diseño similar al siguiente:
2. Utiliza un TableLayout para realizar un diseño similar al siguiente:
3. Utiliza un LinearLayout horizontal que contenga en su interior otros LinearLayout para realizar un diseño similar al siguiente. Ha de ocupar toda la pantalla:
4. Visualiza el resultado obtenido en diferentes tamaños de pantalla. ¿Se visualiza correctamente?
5. Realiza el ejercicio de la calculadora usando un ConstraintLayout.
Preguntas de repaso: ConstraintLayout