En este tutorial aprenderás a configurar el ambiente de desarrollo en Android Studio para agregar Jetpack Compose en proyectos nuevos o existentes.
Como requisito necesitarás la versión Artic Fox de Android Studio con el fin de usar nuevas herramientas y plantillas de actividades asociadas a Compose al crear nuevos proyectos.
Nota: Esta es la primera parte de la guía de Compose (todo) de Develou. En ella encontrarás la definición y propósito de esta API y los demás tutoriales en forma secuencial para aprender a usarla.
Ejemplo Al Agregar Jetpack Compose
Aunque el objetivo de este contenido es familiarizarte con la manera de añadir la dependencia de Compose a tus proyectos, también crearemos un ejemplo simple para el diseño de una pantalla de Login:
La idea es abordar de forma breve el uso de funciones componibles (composable functions), el diseño de layouts y el uso de modificadores para ordenar los elementos visuales. No obstante, en los siguientes tutoriales veremos en detalle estos y otros temas más.
Puedes descargar el ejemplo desde el siguiente enlace:
Con esto en mente, configuremos un nuevo proyecto con Compose.
1. Crear Nuevo Proyecto En Android Studio
Abre Android Studio y selecciona la opción New Project. Si te encuentras en un proyecto actualmente, entonces ve a File>New Project:
Esta acción te desplegará una ventana con la lista de plantillas para dispositivos móviles que la versión del IDE provee. En nuestro caso seleccionaremos Empty Activity. Más adelante veremos que al usar a Compose Empty Activity se simplificará la incorporación de Jetpack Compose al proyecto:
Esta plantilla creará una actividad llamada MainActivity
, la cual renombraremos a LoginActivity
:
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
2. Configurar Android Gradle Plugin
En primer lugar, abre el archivo de build.gradle
del proyecto y especifica una versión igual o mayor a 7.0 para el plugin de Android:
buildscript {
//...
dependencies {
classpath "com.android.tools.build:gradle:7.0.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
}
}
Android Studio Artic Fox usará este valor al crear nuevos proyectos, pero si ya tenías un proyecto existente, entonces actualízalo.
3. Establecer Versión De Compose
Es posible extraer la versión del paquete de Compose con un bloque ext()
desde build.gradle
del módulo, con el fin de reutilizar este valor en todos los módulos que usen la API:
buildscript {
ext {
compose_version = '1.0.1'
}
//...
}
4. Habilitar Jetpack Compose
Abre el archivo build.gradle
del módulo que haga uso de Jetpack Compose para establecer su versión mínima del SDK un nivel igual o mayor a 21.
Luego haz explícita la habilitación de la API desde el bloque buildFeatures
. Y luego asigna la versión del compilador del plugin Kotlin en composeOptions
:
android {
defaultConfig {
minSdk 21
//...
}
//...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
}
}
5. Añadir Dependencias De Compose
Finalmente, para comenzar a definir tu UI programáticamente añade las dependencias hacia los paquetes que requieras usar en tu proyecto:
dependencies {
// Jetpack Compose
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation 'androidx.activity:activity-compose:1.3.0'
//...
}
Los anteriores grupos proveen las siguientes funcionalidades:
androidx.compose.ui:ui
: Contiene a todos los componentes básicos para construir elementos de la interfaz gráficaandroidx.compose.material:material
: Te permite construir UI con los componentes del material designandroidx.compose.ui:ui-tooling
: Provee la capacidad para previsualizar las interfaces construidas con Composeandroidx.activity:activity-compose
: Provee una actividad con la capacidad de acceder a las funcionalidades de Compose
En los próximos tutoriales veremos la inclusión de otros artefactos para el uso de características complementarias de Compose.
6. Definir ComponentActivity
Para que una actividad pueda crear implementaciones basados en los elementos de Compose, extiendela de la clase ComponentActivity
. En nuestro ejemplo LoginActivity.kt
tendría esta definición base:
class LoginActivity : ComponentActivity() {
}
7. Declarar Layout De Actividad
Funciones Componibles (Composables)
A diferencia del sistema de views que usámos con XML, Compose crea jerarquías de elementos visuales a partir de bloques definidos por funciones componibles.
Estas están marcadas por la anotación @Composable
y son convertidas por el compilador de Compose en jerarquías de nodos visuales que son proyectados en la interfaz:
@Composable
fun FuncionComponible(){
// Jerarquía con otras funciones Composables
}
Existen funciones componibles prefabricadas para representar elementos como texto, gráficos, listas, etc. Pero también es posible definir funciones personalizadas para componer tu jerarquía como veremos a continuación.
La Función setContent()
Para definir el layout de una actividad cambia el método setContentView()
por la llamada de la función setContent()
desde onCreate()
. Por ejemplo:
class LoginActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("Login")
}
}
}
La función de extensión ComponentActivity.setContent()
recibe como parámetro una función componible que declara el contenido de la UI.
En este caso dicha función será Text()
. Ella es la encargada de representar texto en pantalla. Al pasarla como el contenido de setContent()
, se mostrará en pantalla el String «Login».
Si ejecutas la App con este código verás el siguiente resultado:
8. Ordenar Elementos En Layout
Para el diseño de la pantalla de inicio de sesión además del texto que ya tenemos, necesitaremos 2 campos de texto para usuario y contraseña; y el botón que inicia la autenticación.
En consecuencia, añade al bloque de contenido de setContent()
las funciones TextField
y Button
:
setContent {
Text("Login", fontSize = 30.sp)
TextField(
value = "",
onValueChange = {},
label = { Text("Usuario") })
TextField(
value = "",
onValueChange = {},
label = { Text("Constraseña") })
Button(onClick = { }) {
Text("Iniciar Sesión")
}
}
En próximos tutoriales veremos en detalle el uso de estos elementos. Por el momento ten presente que para TextField
, value
es el texto actual escrito, onValueChange
la acción a ejecutar cuando se tipea texto y label
es la etiqueta flotante que sugiere el contenido del campo de texto.
En el caso del Button
, requiere como parámetro obligatorio a onClick
para representar la acción a ejecutar cuando se hace click sobre el elemento. Además recibe como último parámetro una función componible para definir su contenido. En este caso, será el texto asociado con su acción a través de Text()
.
Adicionalmente, hemos pasado el parámetro fontSize
de Text()
. Este es el tamaño del texto que se proyectará. Recibe como una instancia del tipo TextUnit
. Y si quieres usar un valor en SP, entonces accede a la propiedad de extensión Int.sp
.
Previsualizar Composable Functions
Al igual que con el editor de layouts XML, Android Studio también te provee tres pestañas de presentación para las funciones componibles que usen la anotación @Preview
.
Para previsualizar nuestro layout crearemos una composable function llamada LoginView
en LoginActivity
y la marcaremos con @Preview
:
@Composable
@Preview
fun LoginView() {
Text("Login", fontSize = 30.sp)
TextField(
value = "",
onValueChange = {},
label = { Text("Usuario") })
TextField(
value = "",
onValueChange = {},
label = { Text("Constraseña") })
Button(onClick = { }) {
Text("Iniciar Sesión")
}
}
Si seleccionas la pestaña Split o Design verás como resultó la organización de nuestros elementos dentro de LoginView()
:
Usar Columnas
La vista previa anterior evidencia que nuestra UI ha sobrepuesto los elementos uno tras otro sin ninguna consideración.
Y justo aquí es donde entran las columnas de Compose. Estas son representadas por la función Column()
, la cual recibe un conjunto de elementos que serán organizados verticalmente en el layout.
Así que invoquemos una columna que cubra nuestros elementos del login:
@Composable
@Preview
fun LoginView() {
Column {
Text("Login", fontSize = 30.sp)
TextField(
value = "",
onValueChange = {},
label = { Text("Usuario") })
TextField(
value = "",
onValueChange = {},
label = { Text("Constraseña") })
Button( onClick = { }) {
Text("Iniciar Sesión")
}
}
}
Si revisas la previsualización esta vez tendremos:
Modificadores
Los modificadores son funciones que extienden las características de los elementos generados por una función componible. Con ellos podremos cambiar propiedades visuales, añadir información de accessibilidad, detectar gestos, etc.
Estos viven el la interfaz Modifier
, donde existen múltiples métodos que pueden ser encadenados para combinar efectos.
Nuestro layout actual aunque está ordenado, no tiene espacios ni alineaciones que mejoren la pulcritud de la UI. Para ordenarlo usaremos los siguientes modificadores:
fillMaxWidth()
yfillMaxHeight()
: Expande el contenido horizontal o verticalmentefillMaxSize()
: Expande ambas dimensiones de contenidopadding()
: Añade un espacio en Dps a todos los bordes del elementoalign()
: Alinea el elemento con respecto a su contenedor
Teniendo esto en cuenta, mejores nuestro layout de la siguiente forma:
- Extender columna en ambas direcciones
- Añadir 16 dps de padding para la columa
- Extender campos de texto horizontalmente
- Centrar horizontalmente al botón de login
La implementación con todos estos modificadores de nuestra función componible LoginView()
se vería así:
@Composable
@Preview
fun LoginView() {
val spaceKeyline = 16.dp
Column(
Modifier
.fillMaxSize()
.padding(spaceKeyline)
) {
Text("Login", fontSize = 30.sp)
Spacer(modifier = Modifier.size(spaceKeyline))
TextField(
value = "",
modifier = Modifier.fillMaxWidth(),
onValueChange = {},
label = { Text("Usuario") })
Spacer(modifier = Modifier.size(spaceKeyline))
TextField(
value = "",
modifier = Modifier.fillMaxWidth(),
onValueChange = {},
label = { Text("Constraseña") })
Spacer(modifier = Modifier.size(spaceKeyline))
Button(modifier = Modifier.align(Alignment.CenterHorizontally), onClick = { }) {
Text("Iniciar Sesión")
}
}
}
Como ves, introducimos un elemento llamado Spacer
que representa un espacio vacío en la jerarquía. De esta forma tendremos el siguiente resultado:
Nota: Lee el tutorial Modificadores en Jetpack Compose (todo) para profundizar en este tema.
9. Procesar Click En Botón
Recordemos que la función Button()
recibe un parámetro llamado onClick
. Este es del tipo función ()->Unit
para que asignes una lambda con las acciones a realizar.
Nuestro objetivo es mostrar un Toast que informe la intención de loguear al usuario cuando el botón sea presionado. Esto podemos lograrlo pasando una lambda como parámetro en LoginView()
y luego asignarla a onClick
:
@Composable
@Preview
fun LoginView(onClick: () -> Unit = {}) {
//...
Button(
modifier = Modifier.align(Alignment.CenterHorizontally),
onClick = onClick
) {
Text("Iniciar Sesión")
}
}
Aquellas funciones componibles cuyos parámetros no tengan valores por defecto, no podrán ser renderizadas en la previsualización. Por esta razón hemos asignado un bloque vacío a onClick
.
Ahora bien, para mostrar el Toast
crearemos un método llamado login()
y pasaremos su referencia desde setContent()
:
class LoginActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LoginView(::login)
}
}
private fun login() {
Toast.makeText(
this,
"Iniciar sesión",
Toast.LENGTH_SHORT
).show()
}
}
Al ejecutar la aplicación podrás ver lo siguiente:
Plantilla Compose Empty Activity
Como había dicho al inicio, la nueva plantilla Compose Empty Activity añade automáticamente toda la configuración de Jetpack Compose en tus archivos Gradle.
Por lo que la actividad creada con ella hereda directamente de ComponentActivity
y trae definido un layout por defecto con un texto de «Hello World» producido por la función componible Greeting()
.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeTemplateTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
ComposeTemplateTheme {
Greeting("Android")
}
}
Adicionalmente crea varios elementos para establecer el tema de la actividad a partir de una función componible en el paquete ui.theme
:
En el tutorial Aplicación de temas con Jetpack Compose (todo) veremos el uso de estos elementos añadidos por la plantilla.