blog
, compose
, csa
Vamos a comenzar con esta serie de publicaciones para conocer, jugar y experimentar con Jetpack Compose, conocer las posibilidades que nos aporta en el desarrollo Android y cómo integrarlo en nuestras aplicaciones. Empezaremos como no con un Hola Mundo
Si aún no lo conocéis, Jetpack Compose es el nuevo framework de programación de interfaces de usuario recientemente anunciado por Google para el futuro del desarrollo de aplicaciones nativas Android. Este framework aprovecha todo el potencial del lenguaje Kotlin para cambiar el estándar de programación de interfaces de usuario proporcionando formas de trabajo más modernas, escalables, intuitivas y potentes:
Compose se integra perfectamente en la arquitectura propuesta por Google a través del conjunto de librerías Android Jetpack. Esta arquitectura se basa en el paradigma MVVM conjuntamente con el concepto de Unidirectional Data Flow (el flujo de datos siempre se realiza en la misma dirección) y Compose tiene intención de aportar un salto de calidad e innovación a la capa de presentación.
Cómo hemos comentado, Compose sustituye a la manera actual de implementar interfaces de usuario:
Históricamente, la jerarquía de vistas de Android representa un árbol de elementos de interfaz de usuario. A medida que el estado de la aplicación cambia, se debe actualizar esta jerarquía para mostrar los datos, de manera manual de forma general. En los últimos años la industria comenzó a migrar a un modelo de interfaz de usuario declarativo con el objetivo de simplificar la ingeniería relacionada con la compilación y actualización de interfaces de usuario. Componse se fundamenta en el enfoque de evitar esta complejidad a la hora de actualizar los datos en la interfaz de usuario mediante composición/recomposición con widgets relativamente sin estado.
Vamos a lo que nos gusta, empecemos a experimentar con el framework.
Para trabajar con Compose, es necesario usar la versión Arctic Fox de Android Studio, aún en Beta: https://developer.android.com/studio/preview
Además, necesitaremos usar las siguientes tecnologías y dependencias:
Para facilitar la tarea, en Android Studio Arctic Fox, viene una plantilla de Compose con la que crearemos nuestro proyecto.
Configuramos el archivo build.gradle del proyecto para usar Gradle 7.0.3 y Kotlin 1.5.31
buildscript {
ext{
gradle_version = '7.2.0'
compose_version = '1.1.1'
kotlin_version = '1.6.10'
androidx_core_version = '1.8.0'
androidx_appcompat_version = '1.4.2'
google_material_version = '1.6.1'
androidx_lifecycle_version = '2.4.1'
androidx_activity_version = '1.4.0'
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:$gradle_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Habilitamos compose en el archivo build.gradle del módulo app, además de definir las dependencias que vamos a usar
android {
// Enable Compose at app module
buildFeatures {
compose true
}
// Configure Compose
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion kotlin_version
}
}
dependencies {
// AndroidX Core & AppCompat dependencies
implementation "androidx.core:core-ktx:$androidx_core_version"
implementation "androidx.appcompat:appcompat:$androidx_appcompat_version"
// Material dependency
implementation "com.google.android.material:material:$google_material_version"
// Lifecycle dependency
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$androidx_lifecycle_version"
// Compose dependencies
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
// Activity-Compose integration dependency
implementation "androidx.activity:activity-compose:$androidx_activity_version"
}
Compose se basa en funciones que se pueden componer. Estas funciones permiten definir la UI de forma pragmática describiendo su forma y dependencias de datos, en lugar de enfocarse en el proceso de construcción de la UI.
Para crear una función que se pueda componer, hay que agregar la anotación @Composable
al nombre de la función.
El bloque setContent
define el diseño de la actividad. En vez de definir el diseño con un archivo en formato XML, llamamos a funciones que se pueden componer. Este bloque está disponible gracias a que nuestra activity extiende de ComponentActivity
de la dependencia Activity-Compose.
Compose usa un complemento de compilador de Kotlin para transformar estas funciones que se pueden componer en elementos de la aplicación. Por ejemplo, Text
es una función definida en la biblioteca de UI de Compose y sirve para declarar un elemento de texto en nuestra aplicación.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Greeting("Android", color = Color.Green)
}
}
}
@Composable
fun Greeting(name: String) {
Text("Hello $name!")
}
Puedes ejecutar la aplicación en un dispositivo o un emulador configurado, pero para ello tendrás que compilar el proyecto (y ya sabemos a veces lo que cuesta eso…).
Para previsualizar los diseños realizados con Compose, al igual que existe la vista preeliminar para los XML clásicos, a partir de la versión Arctic Fox de Android Studio, el IDE trae una opción para mostrar la vista previa de una función componible. La principal restricción es que la función no debe recibir parámetros de entrada. Simplemente hemos de añadir la anotación @Preview
a una función componible:
@Preview
@Composable
fun PreviewGreeting() {
Greeting("Android")
}
Los elementos de la UI son jerárquicos, ya que unos contienen a otros. En Compose, montas una jerarquía de UI llamando a funciones que se pueden componer desde otras funciones que también se puedan componer.
La función APentatonicMinorScale
crea las 5 notas de la escala pentatónica menor de LA como elementos de texto, sin embargo, como no proporcionamos información alguna de la disposición, los elementos se dibujan unos encima de otros.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
APentatonicMinorScale()
}
}
}
@Composable
fun APentatonicMinorScale() {
Text("A")
Text("C")
Text("D")
Text("E")
Text("G")
}
@Preview
@Composable
fun DefaultPreview() {
APentatonicMinorScale()
}
La función Column
nos permite apilar los elementos de forma vertical.
La configuración predeterminada apila todos los elementos secundarios directamente, uno tras otro, sin espacios.
La columna misma se coloca en la esquina superior izquierda de la vista contenido.
@Composable
fun APentatonicMinorScale() {
Column {
Text("A")
Text("C")
Text("D")
Text("E")
Text("G")
}
}
Por último, agregamos estilo a la columna.
modifier
: Permite configurar el diseño. Vamos a aplicar el elemento Modifier.padding
Para enriquecer el diseño y probar nuevas funciones de la biblioteca de Compose, incluimos una Image
height(180.dp)
: indicamos la altura de la imagen.fillMaxWidth()
: La imagen debe ser lo suficientemente ancha como para llenar el diseño al que pertenece.contentScal = ContentScale.Crop
: Especifica que el gráfico de la imagen debe llenar el ancho de la columna y recortar a la altura adecuada, de ser necesario.Por último, vamos a añadir un nuevo elemento, Spacer
para separar la imágen de la escala.
@Composable
fun APentatonicMinorScale() {
Column(
modifier = Modifier.padding(16.dp),
) {
Image(
painter = painterResource(R.drawable.ic_launcher_foreground),
contentDescription = null,
modifier = Modifier
.height(180.dp)
.fillMaxWidth(),
contentScale = ContentScale.Crop
)
Spacer(Modifier.height(16.dp))
Text("A")
Text("C")
Text("D")
Text("E")
Text("G")
}
}
Compose está diseñado para admitir los principios de Material Design. Muchos de los elementos incluídos en la biblioteca lo implementan directamente. Aplicamos forma con uno de los pilares de Material Design como es Shape
. Usaremos la función clip()
para redondear las esquinas de la imagen. Shape
es invisible, pero el gráfico se recorta para ajustarse a él.
Image(
painter = painterResource(R.drawable.ic_launcher_foreground),
contentDescription = null,
modifier = Modifier
.height(180.dp)
.fillMaxWidth()
clip(shape = RoundedCornerShape(4.dp)),
contentScale = ContentScale.Crop
)
Podemos también aplicar mejoras en el diseño de los textos aplicando fuentes, espaciado, etc
@Composable
fun APentatonicMinorScale() {
MaterialTheme {
val typography = MaterialTheme.typography
Column(
modifier = Modifier.padding(16.dp),
) {
Image(
painter = painterResource(R.drawable.header),
contentDescription = null,
modifier = Modifier
.height(180.dp)
.fillMaxWidth(),
contentScale = ContentScale.Crop
)
Spacer(Modifier.height(16.dp))
Text("A Pente¡atonic Minor Scale is a classical Blues and Rock scale",
style = typography.h6,
maxLines = 2,
overflow = TextOverflow.Ellipsis)
Text("A", style = typography.body2)
Text("C", style = typography.body2)
Text("D", style = typography.body2)
Text("E", style = typography.body2)
Text("G", style = typography.body2)
}
}
}
Compose es una tecnología incipiente (a punto de salir la primera release, a lo largo del mes de Julio), y es la gran apuesta de renovación en la pila tecnológica de Android. La mayoría de widgets actuales se han migrado a Compose, pero otros no. Aún así Compose es tan flexible que nos permite de una forma muy sencilla definir cualquier elemento de interfaz (cómo iremos viendo durante la serie).
En resumidas cuentas, Jetpack Compose nace con el propósito de modernizar el desarrollo de interfaces de usuario, simplificar la gestión de estados y la manera de implementarlas e integrarlas con el resto de la arquitectura de la aplicación, permitiendo además construir diseños complejos inalcanzables en la actualidad.
Hemos aprendido a: