En esta página se proporciona una descripción general de alto nivel de las capas de la arquitectura que componen Jetpack Compose, así como los principios básicos que conforman este diseño.
Jetpack Compose no es un proyecto monolítico único, sino que se crea a partir de varios módulos que se ensamblan para formar una pila completa. Si comprendes los diferentes módulos que componen Jetpack Compose podrás:
- Usar el nivel adecuado de abstracción para compilar tu app o biblioteca
- Comprender cuándo puedes "bajar" a un nivel inferior para tener más control o personalización
- Minimizar tus dependencias
Capas
Las capas principales de Jetpack Compose son las siguientes:
Figura 1: Las capas principales de Jetpack Compose.
Se compila cada capa sobre los niveles inferiores y se combinan las funcionalidades para crear componentes de nivel superior. Cada una de ellas toma como fundamento las API públicas de las capas inferiores para verificar los límites del módulo y permitirte reemplazar cualquiera de ellas si es necesario. Examinemos estas capas desde abajo hacia arriba.
- Tiempo de ejecución
- En este módulo se presentan los aspectos básicos del entorno de ejecución de Compose, por ejemplo,
remember
,mutableStateOf
, la anotación@Composable
ySideEffect
. Puedes considerar compilar directamente en esta capa si solo necesitas las capacidades de administración de árbol de Compose, no su IU. - IU
- La capa de la IU consta de varios módulos (
ui-text
,ui-graphics
,ui-tooling
, etc.). Estos módulos implementan los aspectos básicos del kit de herramientas de la IU, comoLayoutNode
,Modifier
, controladores de entrada, diseños personalizados y dibujos. Considera compilar en esta capa si solo necesitas conceptos fundamentales de un kit de herramientas de la IU. - Base
- En este módulo se proporcionan bloques de compilación agnóstica del sistema de diseño para la IU de Compose, como
Row
yColumn
,LazyColumn
, el reconocimiento de gestos determinados, etc. Considera compilar sobre la capa base para crear tu propio sistema de diseño. - Material
- En este módulo se proporciona una implementación del sistema Material Design para la IU de Compose y un sistema de temas, componentes de diseño, indicadores de ondas e íconos. Compila sobre esta capa cuando uses Material Design en tu app.
Principios de diseño
Un principio rector para Jetpack Compose es proporcionar pequeñas piezas enfocadas en la funcionalidad que se puedan ensamblar (o componer) en lugar de unos pocos componentes monolíticos. Este enfoque tiene varias ventajas.
Controla tus dispositivos
Los componentes de nivel superior tienden a hacer más cosas por ti, pero limitan la cantidad de control directo que tienes. Si necesitas más control, puedes "bajar" para usar un componente de nivel inferior.
Por ejemplo, si deseas animar el color de un componente, puedes usar la API de animateColorAsState
:
val color = animateColorAsState(if (condition) Color.Green else Color.Red)
Sin embargo, si necesitabas que el componente comience siempre en gris, no puedes hacerlo con esta API. En su lugar, puedes bajar de nivel y usar la API de Animatable
de nivel inferior:
val color = remember { Animatable(Color.Gray) } LaunchedEffect(condition) { color.animateTo(if (condition) Color.Green else Color.Red) }
La API de animateColorAsState
de nivel superior se compila a partir de la API de Animatable
de nivel inferior. El uso de la API de nivel inferior es más complejo, pero ofrece más control. Elige el nivel de abstracción que mejor se adapte a tus necesidades.
Customization
Si ensamblas componentes de nivel superior con bloques de compilación más pequeños, es mucho más fácil personalizar los componentes en caso de que necesites hacerlo. Por ejemplo, considera la implementación de Button
que proporciona la capa de Material:
@Composable fun Button( // … content: @Composable RowScope.() -> Unit ) { Surface(/* … */) { CompositionLocalProvider(/* … */) { // set LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { Row( // … content = content ) } } } }
Un Button
se ensambla a partir de 4 componentes:
Un objeto
Surface
material que proporciona el fondo, la forma, el control de clics, etcéteraUn objeto
CompositionLocalProvider
que cambia la versión alfa del contenido cuando el botón está habilitado o inhabilitadoUn objeto
ProvideTextStyle
que establece el estilo de texto predeterminado que se usaráUn objeto
Row
que proporciona la política de diseño predeterminada para el contenido del botón
Omitimos algunos parámetros y comentarios a fin de que la estructura sea más clara, pero el componente completo tiene solo unas 40 líneas de código, ya que simplemente ensambla estos 4 componentes para implementar el botón. Los componentes como Button
se expresan sobre qué parámetros muestran, lo que crea un equilibrio entre la habilitación de personalizaciones comunes y una explosión de parámetros que pueden hacer que un componente sea más difícil de usar. Los componentes de Material, por ejemplo, ofrecen personalizaciones especificadas en el sistema de Material Design y, de esta forma, se simplifica seguir sus principios.
Sin embargo, si deseas realizar una personalización más allá de los parámetros de un componente, puedes "bajar" un nivel y bifurcar un componente. Por ejemplo, Material Design especifica que los botones deben tener un fondo de color sólido. Si necesitas un fondo con gradiente, esta opción no es compatible con los parámetros Button
. En este caso, puedes usar la implementación Button
de Material como referencia y compilar tu propio componente:
@Composable fun GradientButton( // … background: List<Color>, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background( Brush.horizontalGradient(background) ) ) { CompositionLocalProvider(/* … */) { // set material LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { content() } } } }
En la implementación anterior, se siguen usando componentes de la capa de material, como los conceptos de la versión alfa del contenido actual y el estilo de texto actual de material. Sin embargo, se reemplaza la Surface
de material con una Row
y la diseña para lograr el aspecto deseado.
Si no deseas usar conceptos de materiales, por ejemplo, si compilas tu propio sistema de diseño a medida, puedes comenzar a usar solo componentes de capas base:
@Composable fun BespokeButton( // … backgroundColor: Color, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background(backgroundColor) ) { // No Material components used content() } }
Jetpack Compose reserva los nombres más simples para los componentes de nivel superior. Por ejemplo, androidx.compose.material.Text
se compila sobre androidx.compose.foundation.text.BasicText
.
Esto te permite proporcionar tu propia implementación con el nombre más detectable si deseas reemplazar niveles superiores.
Elige la abstracción correcta
La filosofía de Compose de crear componentes reutilizables en capas implica que no es necesario que busques siempre los bloques de compilación de niveles inferiores. Muchos componentes de nivel superior no solo ofrecen más funcionalidad, sino que también implementan prácticas recomendadas, como admitir la accesibilidad.
Por ejemplo, si deseas agregar compatibilidad con gestos a tu componente personalizado, puedes compilarlo desde cero mediante Modifier.pointerInput
, pero hay otros componentes de nivel superior compilados sobre este, que pueden ofrecer un mejor punto de partida, por ejemplo, Modifier.draggable
, Modifier.scrollable
o Modifier.swipeable
.
Como regla general, se recomienda que compiles sobre el componente de nivel más alto que ofrece la funcionalidad que necesitas para beneficiarte de las prácticas recomendadas incluidas.
Más información
Consulta la muestra de Jetsnack para ver un ejemplo de cómo compilar un sistema de diseño personalizado.
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Kotlin para Jetpack Compose
- Listas y cuadrículas
- Efectos secundarios en Compose