Desde su nacimiento e instauración, Android ha ido evolucionando en muchos aspectos, siendo los más importantes el cambio de lenguaje a Kotlin, el cambio introducido por las corrutinas y los ViewModels hacia una programación más reactiva. Recientemente, destaca Jetpack Compose, un sistema de creación de interfaces declarativas siguiendo la tendencia de otros lenguajes como React, Flutter o SwiftUI. Vamos a ver en qué consiste este cambio y sus principales características.

¿Qué es Compose?

Como ya se ha dicho, la principal característica de Jetpack Compose es que cambia el paradigma del desarrollo y nos introduce en las interfaces declarativas: sistemas de vistas que utilizan el paradigma de la programación declarativa.

Con Compose decimos cómo queremos que sean las vistas, en lugar de especificar todos los pasos de implementación:

La otra gran característica es que la interfaz está totalmente interconectada con un estado, de tal forma que si ese estado cambia, la interfaz se repinta (o recompone) para representar ese nuevo estado. Además, cabe destacar que Compose es lo suficientemente “inteligente” como para solo reconstruir las partes de la vista que han cambiado y no la vista por completo.

Por ejemplo, con el sistema actual de Android para creación de interfaces (XML), para ocultar una vista normalmente utilizaríamos un método como findViewById() para obtener la referencia a la vista y a continuación llamaríamos al método setVisibility() sobre ella.

Sin embargo, con las interfaces declarativas, describiremos la interfaz como visible o no, y cuando queramos cambiarla, simplemente volveremos a ejecutar el código que describe la UI con dicho cambio.

¿Por qué utilizar Compose?

Por si tienes dudas de si empezar a usar Compose (lo sabemos, es un cambio radical), a continuación presentamos sus principales ventajas:

  1. Menos código.

Nada mejor para demostrarlo que un ejemplo simple de cómo crear un campo de texto de manera tradicional VS Compose:

Forma tradicional:

<TextView
       android:id="@+id/tvExample"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="It’s-a me! Mario!"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

Con Compose:

Text(text = "It's-a me! Mario!")
  1. Librería independiente

La librería de Compose no está acoplada al sistema operativo, cosa que ocurría con los componentes actuales. Si por ejemplo Google actualiza el componente ConstraintLayout, tiene que sacar una nueva versión del SO y esperar a que la mayoría de los dispositivos estén actualizados a esa versión, cosa que puede tardar años.

Con Compose, los desarrolladores pueden añadir nuevos componentes y features sin cambiar la versión del SO. Además, con las nuevas versiones de Jetpack no se “rompen” las aplicaciones hechas con anteriores versiones, ya que no es necesario actualizar la librería de Compose a no ser que se quiera

  1. Compatibilidad con código Legacy (XML)

Por si fuera poco… ¡Es compatible con código Legacy! Podemos hacer convivir nuestra vista construida con Compose con el resto de componentes de un XML. Para ello integramos una vista de tipo ComposeView en el archivo XML del layout y lo referenciamos dentro del código de la app, inflando en su interior la vista que queramos mediante el método setContent():

binding.composeView.apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                // In Compose world
                MaterialTheme {
                    Text("Hello Compose!")
                }
            }
        }
  1. Es… ¿Composable?

Como su propio nombre indica, Compose es… ¿componible? ¿Compuestible? Bueno, Compose es composable, lo que quiere decir que está diseñada de tal manera que podemos construir nuestras interfaces con un sistema de pequeños bloques de código reutilizables e independientes en lugar de a nivel de Fragment o Activity, ya que un componente marcado como @Composable puede albergar más composables en su interior.

Además, cada componente puede estar en un fichero independiente de tipo file, ya que, como ya se ha dicho, no dependen del SO, haciendo su organización y diseño muchísimo más fáciles.

Primeros pasos

La piedra angular de Compose son las funciones @Composable: funciones que admiten composición y que nos permiten definir toda la IU de la app de manera programática (Kotlin) y sin necesidad de XMLs, inicialización de elementos, etc.

Para crear estas funciones tan solo hay que añadir la anotación @Composable al nombre de la función. A pesar de definirse como una función simple, esta se trata de un componente, por lo tanto, deberá empezar por mayúsculas.

A continuación, vamos a crear un texto que admita su contenido de forma dinámica y veremos también la forma de previsualizar lo que estamos diseñando con Compose.

Creando un texto

En primer lugar, necesitaremos la versión más reciente de Android Studio y las dependencias necesarias para diseñar con Compose. Se recomienda buscar directamente las últimas actualizaciones, ya que al estar en una fase temprana de desarrollo y adopción suelen publicar los cambios cada poco tiempo. En el momento de redactar este artículo, las versiones más actualizadas son las que mostramos a continuación, junto con una pequeña indicación de su contenido

dependencies {
    implementation 'androidx.compose.ui:ui:1.2.1'
    // Tooling support (Previews, etc.)
    implementation 'androidx.compose.ui:ui-tooling:1.2.1'
    // Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.)
    implementation 'androidx.compose.foundation:foundation:1.2.1'
    // Material Design
    implementation 'androidx.compose.material:material:1.2.1'
    // Material design icons
    implementation 'androidx.compose.material:material-icons-core:1.2.1'
    implementation 'androidx.compose.material:material-icons-extended:1.2.1'
    // Integration with activities
    implementation 'androidx.activity:activity-compose:1.5.1'
    // Integration with ViewModels
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
    // Integration with observables
    implementation 'androidx.compose.runtime:runtime-livedata:1.2.1'
    implementation 'androidx.compose.runtime:runtime-rxjava2:1.2.1'

    // UI Tests
    androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.2.1'
}

La forma más inmediata de iniciar un nuevo proyecto es seleccionando File -> New -> New Project y en la pantalla de selección elegir Empty Compose Activity, lo cual agregará las últimas dependencias necesarias así como la configuración en gradle:

buildFeatures {
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion compose_version
    }

Los anteriores fragmentos de código pertenecen al fichero gradle a nivel de app.

El siguiente paso será mostrar un texto. Como ya vimos antes, la declaración de un texto simple es inmediata en Compose, por lo que vamos a dar un paso más y a crear nuestra función personalizada que escriba por pantalla un texto. Para ello recordemos utilizar la anotación @Compose antes del nombre de nuestra función:

@Composable
fun Greeting(message: String) {
    Text(text = "I love the smell of $message in the morning...") 
}

Definiendo el contenido

Hemos creado nuestro primer componente con Compose, pero ahora necesitamos dar un paso muy importante: mostrarlo por pantalla.

Para ello nos dirigimos a la clase donde va a ser utilizado (en este ejemplo será MainActivity, la cual hereda de ComponentActivity) y dentro del clásico método onCreate meteremos la llamada a nuestra función dentro del método setContent. Este método es el más relevante dentro de Compose, ya que sin él no seremos capaces de visualizar ningún diseño en pantalla. Solo acepta funciones @Composable en su interior.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Greeting("Kotlin")
        }
    }
}

Como se puede observar, la función Greeting no está dentro del cuerpo de MainActivity, ya que la hemos definido en un fichero independiente para ejemplificar lo dicho anteriormente sobre la independencia y atomicidad de los nuevos componentes.

Previsualización y @Preview

Lo que queremos ahora es tener una vista previa del componente que estamos desarrollando. Para ello, anotaremos una función Composable sin parámetros con la etiqueta @Preview. Esto nos permite previsualizar nuestros componentes sin necesidad de ejecutar la aplicación en un dispositivo o emulador.

En nuestro caso, la función Greeting tiene un parámetro de entrada, así que tendremos que crear una nueva función para encapsular la llamada a nuestro componente:

@Preview
@Composable
fun PreviewGreeting() {
    MessageCard("Kotlin")
}

No está de más recordar/recomendar utilizar la función de preview con todo el contenido que se haya definido dentro de la función setContent para poder previsualizar la pantalla final que estamos diseñando.

Además, podemos definir múltiples funciones Preview, las cuales se mostrarán en la sección Design de Android Studio:

Conclusiones y próximos pasos

En este breve tutorial hemos hecho una introducción a los conceptos básicos de Jetpack Compose, pero falta mucho camino. Aquí podéis ver cómo modificar y personalizar una vista como queramos mediante el uso de Modifier y Material Design, cómo organizar las vistas dentro de contenedores (layouts) y, por último, la gestión de estados y la recomposición de componentes cuando dicho estado se ve alterado.

Como veis, no hemos hecho más que abrir el melón del futuro del diseño en Android y espero que os haya gustado/seducido lo suficiente como para darle una oportunidad. Si os interesa el tema, no perdáis de vista nuestro blog, pronto os compartimos la segunda parte: Diseñando UIs con Compose.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.