Cómo alinear un botón a la izquierda en Android Studio con Jetpack Compose

portalcenter.cl

En Jetpack Compose, la alineación de un botón depende principalmente del contenedor que lo incluye, del modificador utilizado y de la forma en que se distribuyen sus elementos internos. Para colocar un botón a la izquierda, normalmente se utiliza un layout horizontal y se controla la posición mediante modificadores de alineación.

Los botones son elementos sencillos de utilizar y, obviamente, permiten interactuar con la aplicación, porque para eso estamos creando una aplicación en Android.

Botones en Android Studio y Jetpack Compose

Muchos recursos sobre botones Android muestran la forma antigua o legacy, implementando interfaces con XML (View System). Vendría siendo algo similar a una columna si vienes de otras tecnologías.

En Jetpack Compose puedes colocar botones normales, botones con iconos y otros estilos. Todos funcionan igual a nivel de lógica, ya que todos tienen el evento onClick. Lo que cambia principalmente es el estilo visual.

  • Botón lleno (Filled): fondo sólido con texto con contrastante.
  • Botón tonal: el color de fondo varía para coincidir con la superficie.
  • Botón elevado (Elevated): botón con sombra.
  • Botón con borde (Outlined): botón con borde.
  • Botón con icono: botón con ícono.

Luego definimos una función normal en Kotlin, como ya vimos en la introducción básica del lenguaje. El nombre puede ser cualquiera. Yo la voy a usar para definir varios botones, pero de momento será uno solo.

Este onClick recibe una función lambda que no retorna nada (Unit).

Alinear un botón a la izquierda dentro de un layout horizontal

Para colocar un botón a la izquierda dentro de una fila, el contenedor debe ocupar el espacio horizontal disponible y utilizar una alineación que coloque sus elementos al inicio.

En un diseño horizontal, una fila permite organizar sus elementos uno al lado del otro. Si necesitas que un botón quede a la izquierda, el layout debe utilizar una disposición inicial para sus elementos.

También puedes utilizar un Modifier para controlar el tamaño del contenedor y otro Modifier para establecer la alineación del botón dentro de ese espacio.

Botón alineado a la izquierda

Una estructura horizontal puede utilizarse para colocar un botón al principio del espacio disponible:

Row permite organizar los elementos horizontalmente, mientras que la alineación inicial coloca el botón en el lado izquierdo del layout.

Si el contenedor no ocupa todo el ancho disponible, la alineación solo tendrá efecto dentro del espacio que realmente tenga el contenedor. Por eso es importante que el layout pueda ocupar la pantalla horizontalmente.

Cómo ocupar toda la pantalla horizontalmente

Si tienes cinco botones independientes y necesitas que aparezcan centrados en un layout horizontal, es necesario diferenciar entre dos comportamientos:

  • Colocar todos los botones en una sola fila.
  • Colocar un botón a la izquierda dentro de una fila.

Cuando varios botones se encuentran dentro de una misma fila, el ancho disponible debe distribuirse entre ellos. Si los botones ocupan más espacio del disponible, pueden verse apretados o desbordarse.

En pantallas pequeñas muchos botones en fila pueden verse apretados. En ese caso, puede ser más conveniente distribuirlos, reducir el espacio interno o permitir que vuelvan a apilarse.

Si tienes los botones en columnas distintas de la misma fila, ya quedan uno al lado del otro naturalmente por el layout de columnas.

Distribuir varios botones horizontalmente

Cuando varios elementos deben distribuirse horizontalmente, el layout puede asignar espacio a cada botón mediante modificadores de peso. Esta variante es más robusta para diseños donde los botones tienen textos de longitud variable, porque distribuye el espacio automáticamente.

También puedes ajustar el ancho mínimo de los elementos:

.et_pb_button_module_wrapper { flex: 1; min-width: 160px; }

El valor anterior procede de un ejemplo basado en Flexbox y no corresponde directamente a un modificador de Jetpack Compose, pero la idea de distribuir el espacio automáticamente también es aplicable a los layouts horizontales de Compose.

Spacer y espacio entre elementos

El Spacer que usamos sirve para agregar espacio entre el icono y el texto del botón.

Es el valor estándar que recomienda Material Design, pero puedes ajustarlo si quieres:

Modifier.size(ButtonDefaults.IconSpacing)

Por ejemplo:

  • Aumentar el espacio entre elementos.
  • Reducir el padding.
  • Ajustar márgenes internos.

Esto es muy parecido a los margin y padding del desarrollo web tradicional, pero usando unidades propias de Android.

Alinear un botón con un icono y un texto

En un proyecto en el que se utiliza Jetpack Compose, uno de los retos habituales consiste en alinear elementos, como un icono con textos de diferentes tamaños y tipos de fuentes.

Al principio puede parecer sencillo utilizar firstBaseLine, pero esta solución no siempre cumple con los requisitos visuales. En algunos casos, el icono debe estar centrado horizontalmente con la primera línea de texto del componente, no alineado con la base del texto.

También pueden surgir otras necesidades:

  • Adaptar el diseño cuando el usuario activa la opción de accesibilidad de fuentes más grandes.
  • Alinear correctamente un texto con múltiples líneas.
  • Alinear el icono con la última línea del texto.

AlignmentLines: qué son

Para entender cómo alinear elementos en Jetpack Compose, tenemos que aprender qué son las AlignmentLines.

Primero, debemos saber que tenemos dos tipos: VerticalAlignmentLine y HorizontalAlignmentLine.

VerticalAlignmentLine nos permite alinear elementos en una línea vertical dentro de un layout, y se declara sobre el eje X de coordenadas de un componente.

HorizontalAlignmentLine nos permite alinear elementos en una línea horizontal, y se declara sobre el eje Y de coordenadas de un componente.

Las líneas de alineación personalizadas que se utilizan en la creación de nuestro ejemplo son del tipo HorizontalAlignmentLine, ya que su función es alinear verticalmente los elementos secundarios.

Se pasa una política de combinación como parámetro en caso de que varios diseños proporcionen un valor para estas líneas de alineación.

Mientras las coordenadas del sistema de diseño de Compose y las coordenadas de Canvas representen [0, 0], la esquina superior izquierda y los ejes x y y serán positivos hacia abajo, para que el valor MaxChartValue siempre sea menor que MinChartValue.

AlignmentLines en Jetpack Compose

firstBaseLine para alinear textos

Dentro de Jetpack Compose hacemos uso de una HorizontalAlignmentLine incluida en el componente Text, para alinear dos textos de distintos tamaños sobre la base de ambos: firstBaseLine, para lograrlo tenemos que usar el Modifier.alignFirstBaseline() en ambos componentes.

Esta solución es apropiada cuando el objetivo es alinear los textos por su línea base. Sin embargo, no resuelve el caso en el que el icono debe quedar centrado respecto al centro de la primera línea de texto.

Crear un AlignmentLine personalizado

En nuestro caso, necesitamos centrar el icono al centro de la primera línea de un Text component, no tenemos ningún HorizontalAlignmentLine que podamos usar, por lo que vamos a crear una.

Para crear un nuevo AlignmentLine se declara de la siguiente manera.

Al declarar CenterAligmentLine, tenemos un lambda que nos provee dos parámetros, old y new. Estos valores son las coordenadas Y de los elementos que queremos alinear: old nos indica el valor anterior antes de alinear el elemento, y new, el nuevo valor para alinear nuestro componente a otros componentes.

Si llegáramos a crear un VerticalAligmentLine, los valores de old y new serían las coordenadas del eje X del componente.

Lo siguiente es indicar la posición en la que queremos que se encuentre dentro de nuestro componente.

Modifier.layout: indicar la posición del AlignmentLine

Para indicar la posición donde queremos que CenterAligmentLine se encuentre dentro de un componente, tenemos que hacer uso de Modifier.layout.

Creamos un nuevo Modifier.createCenterAlignmentLine, que incluye Modifier.layout, la línea que nos interesa es la línea 6, donde indicamos una lista que contiene un map de objetos Pair, indicando el AlignmentLine con su respectivo valor.

Al ser una línea horizontal, el valor se encuentra sobre el eje Y del componente.

Si no conoces el uso de Modifier.layout, te recomiendo ver la documentación oficial.

Obtener la posición del centro del texto

Para declarar CenterAligmentLine en el componente Text, tenemos que obtener las coordenadas del texto al cual queremos alinear el icono.

Para eso vamos a usar otra función del componente Text: onTextLayout.

Dentro de la función lambda hacemos uso del método getLineTop y getLineBottom, que nos devuelve la posición superior e inferior de la línea que indiquemos como parámetro de la función.

Al ser la primera línea de texto de la cual queremos obtener las coordenadas, usamos el índice 0.

Una vez que tenemos los valores, los vamos a pasar como parámetros a nuestro Modifier para calcular la posición de CenterAligmentLine.

Si quisiéramos obtener las posiciones de otra línea, solo tenemos que cambiar el índice que solicitamos dentro de onTextLayout.

Lo siguiente es aplicar el Modifier en el componente Text que vamos a utilizar para alinear.

Obtener la posición del centro del icono

Al igual que en el componente Text, también tenemos que indicar la posición del centro del componente Icon.

Esto es mucho más sencillo, ya que solo tenemos que obtener el alto del componente y dividirlo para obtener el punto medio.

De la misma manera, aplicamos el Modifier en el componente Icon que vamos a alinear.

AlignBy: momento de alinear los elementos

Una vez que indicamos las posiciones de CenterAlignmentLine de ambos elementos, lo siguiente es indicar que queremos alinear los elementos con el modificador Modifier.alignBy(CenterAlignmentLine).

Cuando ambos elementos utilizan la misma línea de alineación, Jetpack Compose puede colocarlos de acuerdo con la posición calculada.

¡Listo! Ahora nuestros elementos se están alineando en las posiciones que indicamos en los pasos anteriores.

Los elementos superiores directos e indirectos de este elemento que admite composición pueden consumir las líneas de alineación.

El modelo de diseño de Compose te permite usar AlignmentLine para crear líneas de alineación personalizadas que los diseños de nivel superior pueden usar para alinear y posicionar sus elementos secundarios.

Cuando un diseño proporciona un valor para una AlignmentLine específica, los elementos superiores del diseño pueden leer este valor después de la medición con el operador Placeable.get en la instancia Placeable correspondiente.

Algunos elementos de Compose que admiten composición ya incluyen líneas de alineación.

Ejemplo conceptual con datos de gráficos

En el siguiente ejemplo, se muestra un elemento BarChart que admite composición y muestra dos líneas de alineación, MaxChartValue y MinChartValue, de modo que otros elementos que admitan composición puedan alinearse con el valor de datos máximo y mínimo del gráfico.

val maxValue: Float = remember(dataPoints) { dataPoints.maxOrNull()!! }

Custom AlignmentLines are set here.

El siguiente elemento que admite composición crea un diseño personalizado que toma como parámetro dos ranuras y datos Text, y alinea los dos textos con los valores de datos de los gráficos máximo y mínimo.

Iconos y dependencias adicionales

Para usar botones con iconos, necesitamos agregar una dependencia adicional: la librería de Material Icons Extended.

Esto se hace desde el archivo Gradle, en la sección de dependencias.

androidx-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }

Una vez agregada, sincronizamos el proyecto y ya podemos importar los iconos sin problemas.

Después de importar todo correctamente, los errores desaparecen y el Preview se actualiza.

Diseño adaptable y accesibilidad

La alineación debe comprobarse con diferentes tamaños de fuente, especialmente cuando el usuario tiene activada la opción de accesibilidad de fuentes más grandes.

Cuando el texto tiene múltiples líneas, la posición de cada línea puede obtenerse mediante getLineTop y getLineBottom utilizando el índice correspondiente.

Para alinear el icono con la última línea de texto, solo tenemos que cambiar el índice que solicitamos dentro de onTextLayout.

En pantallas pequeñas muchos botones en fila pueden verse apretados. En ese caso, puede ser conveniente adaptar el comportamiento del layout para controlar el comportamiento en móvil.

El mismo principio aplica a otros elementos visuales: puedes utilizar una línea de alineación personalizada cuando una alineación estándar no representa correctamente la relación visual que necesitas.

Recursos y documentación oficial de Android

El ecosistema de Android es muy interesante, sobre todo por todo el tiempo que lleva existiendo.

Puedes buscar perfectamente algo como “botones Android” o “Android Studio botones”. Te recomiendo que lo hagas para que no te aparezcan otros tipos de botones que no nos interesan.

Vas a encontrar muchísima información.

También encontrarás artículos oficiales y de terceros. Algunos muestran implementaciones con XML y otros utilizan Jetpack Compose.

Lo importante es distinguir el sistema de vistas basado en XML de la interfaz declarativa de Jetpack Compose, porque los modificadores de alineación utilizados no son los mismos.

Como hacer Botones y Textos en Jetpack Compose - Buttons y Text

Preguntas frecuentes sobre la alineación de botones

¿Cómo alineo un botón a la izquierda?

Coloca el botón dentro de un layout horizontal que ocupe el espacio disponible y utiliza una disposición inicial para sus elementos. La alineación inicial coloca el botón en el lado izquierdo del layout.

¿Cómo puedo colocar varios botones en una fila?

Coloca los botones dentro de un layout horizontal y distribuye el espacio según el diseño que necesites. Si tienes los botones en columnas distintas de la misma fila, ya quedan uno al lado del otro naturalmente por el layout de columnas.

¿Qué ocurre si los botones están en columnas diferentes?

El CSS no aplica entre columnas - opera solo dentro de la columna que tiene la clase asignada. Si tienes los botones en columnas distintas de la misma fila, ya quedan uno al lado del otro naturalmente por el layout de columnas. Este truco es específicamente para cuando tienes dos o más botones dentro de la misma columna.

En Jetpack Compose, la distribución depende del layout utilizado y de los modificadores aplicados a cada componente.

¿Puedo alinear un icono con la primera línea de un texto?

Sí. Puedes crear una HorizontalAlignmentLine personalizada, obtener la posición de la primera línea mediante onTextLayout y utilizar Modifier.alignBy(CenterAlignmentLine) en el texto y en el icono.

¿Puedo alinear el icono con la última línea?

Sí. Para obtener las posiciones de otra línea, solo tienes que cambiar el índice que solicitas dentro de onTextLayout.

¿Qué ocurre cuando el texto tiene varias líneas?

Los métodos getLineTop y getLineBottom permiten obtener la posición superior e inferior de la línea indicada. De esta forma puedes seleccionar la primera línea, la última línea o cualquier otra línea disponible.

¿Funciona con fuentes grandes?

La implementación debe probarse con la opción de accesibilidad de fuentes más grandes. La posición se calcula a partir de las métricas reales del texto, por lo que es necesario verificar que el icono y el botón mantengan la relación visual esperada.

¿Cómo agrego iconos a los botones?

Para usar botones con iconos, agrega la librería de Material Icons Extended desde el archivo Gradle, sincroniza el proyecto e importa los iconos necesarios.

¿Puedo usar este método con imágenes?

El mismo principio aplica: reemplaza el selector del módulo que contiene tus imágenes y usa la misma clase de alcance en la columna. En Jetpack Compose, el mismo concepto puede aplicarse a otros componentes que deban compartir una línea de alineación.

tags: #alinear #boton #izquierda #android #studio