Jetpack Compose II: Funciones @Composable
En el artículo anterior hicimos un repaso sobre los motivos y objetivos detrás del desarrollo de Jetpack Compose que Google y Jetpack encaran en conjunto. Si no lo leíste te recomiendo que le pegues una mirada por acá:
Ahora nos toca entrar en un terreno más interesante: configurar un entorno, empezar a hacer las primeras pruebas con el framework y entender cómo funcionan el mismo.
Antes de empezar recuerda que Jetpack Compose se encuentra en franco desarrollo y aún no hay ni siquiera liberada una versión alpha, es por esto que intentaré no profundizar demasiado en cómo se usan los componentes disponibles hasta el momento ni hacer un catálogo de los mismos, ya que es muy probable que estos cambien y por el momento no sería útil documentarlos en detalle.
Instalación del entorno
Actualizado en Octubre de 2019: Durante el Android Dev Summit 2019 se liberó la Preview de Android Studio 4.0. Y el equipo de Compose publicó los artefactos Maven, así que ahora es muy sencillo configurar un entorno.
Con Android Studio 4.0 creemos un proyecto nuevo y utilicemos “Empty Compose Activity” como template y… ¡listo! Este proyecto recién creado ya trae incluídas las librerías de Compose y el plugin del compilador de Kotlin.
Nota importante: Las versiones están marcadas como -dev02, por lo tanto tengamos en cuenta siempre que esto ni siquiera es una versión alpha, es de esperarse que haya muchos más cambios en la API.
Nuestra primer Activity con Jetpack Compose
Este artículo está dirigido a desarrolladores que ya tienen experiencia en Android, por lo cual se asumen ciertos conocimientos. Si no es tu caso, te recomiendo que comiences por aquí.
Una vez creado y configurado el proyecto, modificaremos el archivo MainActivity.kt con el siguiente contenido básico y sencillo:
- ¡Listo! Ya tenemos nuestra primer app de ejemplo, que imprime un saludo en pantalla.
Analizando la sintaxis
Lo primero que vamos a notar es que en lugar de llamar a setContentView(int resId) se llama a la extension function que agregaron a Activity: setContent, la que recibe como parámetro un lambda del tipo @Composable.
No usaremos para nada el CompositionContext que retorna esta función, pero debemos saber que básicamente lo que hará es poner un FrameLayout en la Activity y utilizar la lambda recibida para dibujar los elementos.
Nuestra función @Composable
No parece haber mucho de especial en esto, simplemente es una función con tipo de retorno Unit, marcada con la anotación @Composable, la cual en su interior llama a la función Text() pasando un String como parámetro.
¿Qué hace la anotación @Composable? Básicamente genera el código repetitivo (boilerplate) que Compose necesita para generar el árbol del layout, guardar en caché las vistas para detectar específicamente cuáles son las que cambiaron dentro de dicho árbol y guardar aquellas variables que sirvan para almacenar el estado de la vista. Hablaremos sobre cómo manejar el estado de las vistas en otro artículo, por ahora nos enfocaremos en ver qué podemos hacer con las funciones @Composable.
¿Y qué es la función “Text()”? Es una de las tantas funciones para dibujar widgets en pantalla que Jetpack Compose trae. Nótese que sólo estamos pasando el String que queremos que se escriba, pero en realidad la función puede recibir otros parámetros para personalizar cómo se visualizará:
Text() != TextView()
Quizás no lo hayas notado, pero hay algo raro acá… Jetpack Compose rompe con la convención de código de Kotlin al hacer que una función empiece con letra mayúscula (Text). Esto podría generar confusión y creer que estamos creando un objeto que se transforma en un View que se renderiza en la pantalla, pero realmente no es así… la función Text(), al igual que todas las funciones @Composable que dibujan widgets, retornan Unit (el equivalente a void en Java).
Pero entonces, si la función Text() no crea un TextView, ¿qué hay en la pantalla? 🤔
Para comprender la diferencia, cambiemos un momento nuestra Activity por el siguiente código:
Y pongamos este contenido en el archivo activity_my_compose_journey.xml:
De esta forma, usaremos el Layout Inspector para ver la diferencia entre ambos layouts.
Esto es lo que se ve en el caso de la Activity usando el layout en el xml:
Y esto es lo que e ve cuando usamos setContent con Jetpack Compose:
La diferencia está marcada en las screenshots: mientras que cuando usamos layouts de forma convencional se crea un TextView y se agrega al árbol, en la versión que utiliza Jetpack Compose se inserta un FrameLayout que contiene un AndroidComposeView y no se pueden diferenciar los Views que este contiene. Si hacemos la prueba agregando más funciones @Composable que usen Text veremos que sólo se diferencia el mismo AndroidComposeView. Esto tiene total sentido si recordamos lo que decíamos en el artículo anterior: Jetpack Compose no depende del SDK de Android, por lo tanto no dibuja Views, ¿qué hace?: directamente invoca a drawRect() sobre el canvas: dibuja el contenido, pero no tiene absolutamente ninguna dependencia con los Views de Android. Internamente, Compose utiliza un objeto llamado Composer que sabe renderizar la función Text en Android, y lo más interesante de todo es que a futuro se podrán crear distintas implementaciones de Composers para utilizar Jetpack Compose en cualquier plataforma en la que se pueda ejecutar código Kotlin… 🤔 hay un gran potencial para Kotlin/Native y Kotlin/Multiplatform en este punto. Si no estás al tanto de este proyecto, te recomiendo que revises la documentación oficial, o que mires la charla de Yury Camacho en el Kotlin/Everywhere Buenos Aires.
¿Qué se puede hacer en una función @Composable?
Bueno… ¡lo que quieras! Son simplemente funciones de Kotlin, por lo tanto podemos hacer cosas como…
O bien hacerlas recibir parámetros necesarios para la UI:
O también…
Y por último el concepto más importante: la idea detrás de todo esto es la nueva forma en la que debemos pensar: nuestra UI debe utilizar funciones @Composable que a su vez estén compuestas por otras funciones @Composable, como se ve en el siguiente ejemplo:
Esta ha sido nuestra primera aproximación práctica a Jetpack Compose. Lo que hemos visto representa la estructura básica de una UI y hemos analizado el código y mencionado algunos detalles de implementación. En el siguiente artículo aprenderemos sobre el flujo recomendado de datos y eventos para nuestras aplicaciones e iremos entendiendo mejor el potencial de esta tecnología.
Otros links de interés
- Compose From First Principles: http://intelligiblebabble.com/compose-from-first-principles
- Android Developers Backstage Podcast: http://androidbackstage.blogspot.com/2019/06/episode-115-jetpack-compose.html
- Karumi blog: https://blog.karumi.com/android-jetpack-compose-review
- Jetpack Compose Quick Setup: https://android.jlelse.eu/jetpack-compose-quick-setup-d9b40a2e539b