Jetpack Compose facilita mucho el diseño y la compilación de la IU de tu app. Compose transforma el estado en elementos de la IU mediante lo siguiente:
- Composición de elementos
- Diseño de elementos
- Dibujo de elementos
Este documento se centra en el diseño de los elementos. Explica algunos de los componentes fundamentales que proporciona Compose a fin de ayudarte a diseñar los elementos de tu IU.
Objetivos de los diseños en Compose
La implementación de Jetpack Compose del sistema de diseño tiene dos objetivos principales:
- Alto rendimiento
- Capacidad de escribir fácilmente diseños personalizados
Conceptos básicos de las funciones de componibilidad
Las funciones que admiten composición son los componentes fundamentales de Compose. Una función de este tipo emite una Unit
que describe alguna parte de tu IU. La función toma alguna entrada y genera lo que se muestra en la pantalla. Para obtener más información sobre elementos componibles, consulta la documentación sobre el modelo mental de Compose.
Una función de componibilidad podría emitir varios elementos de la IU. Sin embargo, si no indicas cómo deben organizarse, es posible que Compose lo haga de una forma que no te agrade. Por ejemplo, este código genera dos elementos de texto:
@Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") }
Si no tiene indicaciones sobre cómo quieres organizarlos, Compose los apila uno encima del otro y resultan ilegibles:
Compose proporciona una colección de diseños listos para usar que te ayudan a organizar los elementos de la IU y facilitan la definición de tus propios diseños más especializados.
Componentes de diseño estándar
En muchos casos, puedes usar los elementos de diseño estándar de Compose.
Usa Column
para colocar elementos en sentido vertical en la pantalla.
@Composable fun ArtistCardColumn() { Column { Text("Alfred Sisley") Text("3 minutes ago") } }
Del mismo modo, usa Row
para colocar los elementos en sentido horizontal en la pantalla. Tanto Column
como Row
admiten la configuración de gravedad de los elementos que contienen.
@Composable fun ArtistCardRow(artist: Artist) { Row(verticalAlignment = Alignment.CenterVertically) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { Text(artist.name) Text(artist.lastSeenOnline) } } }
Usa Box
para colocar un elemento sobre otro. Además, Box
admite la configuración de la alineación específica de los elementos que contiene.
@Composable fun ArtistAvatar(artist: Artist) { Box { Image(bitmap = artist.image, contentDescription = "Artist image") Icon(Icons.Filled.Check, contentDescription = "Check mark") } }
Estos componentes fundamentales suelen ser todo lo que necesitas. Puedes escribir tu propia función que admita composición para combinar esos diseños en uno más elaborado que se adapte a tu app.
Para establecer la posición de los elementos secundarios dentro de un Row
, configura los argumentos horizontalArrangement
y verticalAlignment
. Para un objeto Column
, configura los argumentos verticalArrangement
y horizontalAlignment
:
@Composable fun ArtistCardArrangement(artist: Artist) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.End ) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { /*...*/ } } }
Modelo de diseño
En el modelo de diseño, el árbol de IU se presenta en un solo pase. Primero, se solicita que cada nodo se mida, luego, que mida los elementos secundarios de manera recurrente y que pase las restricciones de tamaño por el árbol hasta el elemento secundario. Luego, se establece el tamaño y la posición de los nodos de hoja, y las instrucciones sobre el tamaño y la posición resueltas se vuelven a pasar al árbol.
En pocas palabras, los elementos superiores realizan la medición antes que sus elementos secundarios, pero su tamaño y posición se establecen después de sus elementos secundarios.
Considera la siguiente función SearchResult
.
@Composable fun SearchResult() { Row { Image( // ... ) Column { Text( // ... ) Text( // ... ) } } }
Esta función muestra el siguiente árbol de IU.
SearchResult
Row
Image
Column
Text
Text
En el ejemplo de SearchResult
, el diseño del árbol de IU sigue este orden:
- Se solicita que el nodo raíz
Row
realice la medición. - El nodo raíz
Row
le solicita a su primer elemento secundario,Image
, que realice la medición. Image
es un nodo de hoja (es decir, no tiene elementos secundarios), por lo que informa un tamaño y devuelve instrucciones sobre la posición.- El nodo raíz
Row
le solicita al segundo elemento secundario,Column
, que realice la medición. - El nodo
Column
le solicita al primer elemento secundarioText
que realice la medición. - El primer nodo
Text
es un nodo de hoja, por lo que informa un tamaño y devuelve instrucciones sobre la posición. - El nodo
Column
le solicita a su segundo elemento secundarioText
que realice la medición. - El segundo nodo
Text
es un nodo de hoja, por lo que informa un tamaño y devuelve instrucciones sobre la posición. - Ahora que el nodo
Column
realizó la medición de sus elementos secundarios, estableció sus tamaños y sus posiciones, puede determinar su propio tamaño y posición. - Ahora que el nodo raíz
Row
realizó la medición de sus elementos secundarios, estableció sus tamaños y sus posiciones, puede determinar su propio tamaño y posición.
Rendimiento
Compose logra un alto rendimiento midiendo los elementos secundarios solo una vez. La medición de un solo paso es ideal en términos de rendimiento y permite que Compose procese de manera eficiente los árboles detallados de la IU. Supongamos que un elemento midió dos veces a su elemento secundario, y el elemento secundario, a su vez, midió dos veces cada uno de sus elementos secundarios, y así sucesivamente. Un solo intento para implementar toda la IU requeriría muchísimo trabajo, lo que dificultaría lograr que tu app funcione bien.
Si tu diseño necesita varias mediciones por algún motivo, Compose ofrece un sistema especial: mediciones intrínsecas. Puedes obtener más información sobre esta función en Medición intrínseca en los diseños de Compose.
Dado que la medición y la posición son subfases distintas del pase de diseño, cualquier cambio que solo afecte a la ubicación de los elementos, y a no la medición, se puede ejecutar por separado.
Cómo puedes usar modificadores en tus diseños
Como se indicó en Modificadores de Compose, puedes usar modificadores para decorar o aumentar tus elementos componibles. Los modificadores son esenciales para personalizar tu diseño. Por ejemplo, aquí encadenamos varios modificadores para personalizar ArtistCard
:
@Composable fun ArtistCardModifiers( artist: Artist, onClick: () -> Unit ) { val padding = 16.dp Column( Modifier .clickable( .padding(padding) .fillMaxWidth() ) { Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ } Spacer(Modifier.size(padding)) Card( elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), ) { /*...*/ } } }
En el código anterior, observa distintas funciones de modificadores que se usan juntas.
clickable
hace que un elemento componible reaccione a la entrada del usuario y muestre una onda.padding
coloca espacio alrededor de un elemento.fillMaxWidth
hace que el elemento componible ocupe el ancho máximo que le otorga su elemento superior.size()
especifica el ancho y la altura preferidos de un elemento.
Diseños desplazables
Obtén más información sobre los diseños desplazables en la documentación sobre gestos de Compose.
Para obtener información sobre listas y listas Lazy, consulta la documentación sobre listas de Compose.
Diseños receptivos
Un diseño debe tener en cuenta diferentes orientaciones de pantalla y tamaños de factores de forma. Compose ofrece configuraciones integradas de forma inmediata para facilitar la adaptación de tus diseños componibles a diferentes configuraciones de pantalla.
Restricciones
Para conocer las restricciones que provienen del elemento superior y diseñar el diseño según corresponda, puedes usar BoxWithConstraints
. Las restricciones de medición se pueden encontrar en el alcance de la lambda de contenido. Puedes usar estas restricciones de medición a fin de componer diferentes diseños para distintas configuraciones de pantalla:
@Composable fun WithConstraintsComposable() { BoxWithConstraints { Text("My minHeight is $minHeight while my maxWidth is $maxWidth") } }
Diseños basados en ranuras
Compose proporciona una gran variedad de elementos componibles basados en Material Design con la dependencia androidx.compose.material:material
(que se incluye cuando se crea un proyecto de Compose en Android Studio) para facilitar la compilación de IU. Estos elementos incluyen Drawer
, FloatingActionButton
y TopAppBar
.
Los componentes de Material usan mucho las API de ranuras, un patrón que introduce Compose para agregar una capa de personalización sobre elementos componibles. Este enfoque hace que los componentes sean más flexibles, ya que aceptan un elemento secundario que puede configurarse automáticamente, en lugar de tener que exponer cada parámetro de configuración del elemento secundario.
Las ranuras dejan un espacio vacío en la IU para que el desarrollador lo complete como quiera. Por ejemplo, estas son las ranuras que puedes personalizar en una TopAppBar
:
Los elementos componibles suelen adoptar una expresión lambda content
componible (content: @Composable
() -> Unit
). Las APIs con ranuras exponen varios parámetros de content
para usos específicos.
Por ejemplo, TopAppBar
te permite proporcionar el contenido para title
, navigationIcon
y actions
.
Por ejemplo, Scaffold
te permite implementar una IU con la estructura básica de diseño de Material Design. Scaffold
proporciona ranuras para los componentes de Material de nivel superior más comunes, como TopAppBar
, BottomAppBar
, FloatingActionButton
y Drawer
. Si usas Scaffold
, es fácil asegurarte de que esos componentes estén bien posicionados y funcionen de forma correcta.
@Composable fun HomeScreen(/*...*/) { ModalNavigationDrawer(drawerContent = { /* ... */ }) { Scaffold( topBar = { /*...*/ } ) { contentPadding -> // ... } } }
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Modificadores de Compose
- Kotlin para Jetpack Compose
- Componentes y diseños de Material