Cargar imágenes e iconos en Jetpack Compose
Antonio Leiva

Algo que vas a tener que hacer casi siempre en una App es cargar imágenes, ya sea desde un servidor remoto, o desde tu propio dispositivo.

En el sistema clásico de vistas nunca existió una forma «oficial», y siempre hemos tenido que recurrir a librerías de terceros.

Y… ¡Oh, sorpresa! en Jetpack Compose ocurre lo mismo.

Durante un tiempo, en la librería de Accompanist, una librería donde se van añadiendo funcionalidades experimentales para Compose, se daba soporte para tres librerías: Picasso, Glide y Coil.

Pero finalmente se decidió eliminar esos adaptadores de Accompanist, y recomiendan en su lugar utilizar la integración ya incluida en la librería de Coil.

Así que en este artículo vamos a ver cómo usar Coil, y también cómo cargar iconos que nos provee la librería de Compose.

Recuerda que nuestro objetivo es conseguir algo como esto:

En el artículo anterior hemos visto cómo hacer todo usando modifiers, excepto cargar las imágenes. Así que vamos a ello:

¡Atención! Si quieres acceder más rápido a todo el contenido en vídeo, organizado, con contenido extra, soporte y muchas más sorpresas, puedes apuntarte gratis a mi formación Compose Expert y ver gratis el primer módulo de más de 3 horas con todo lo necesario para empezar.

Cargar imágenes con Coil

El Composable que nos permite cargar imágenes tiene un nombre bien sencillo: Image

Tenemos varias formas de cargar una imagen, por ejemplo a partir de un bitmap, pero la más habitual será la de usar un painter.

El painter define lo que hay que pintar en la Image. La librería de Coil va más allá y nos ofrece su propia variante de Image llamada AsyncImage, que de forma muy sencilla nos permitirá cargar imágenes.

En la documentacion para usar Coil en Jetpack Compose se nos indica que lo primero que tenemos que hacer es añadir una dependencia:

implementation 'io.coil-kt:coil-compose:2.0.0'

Tampoco te olvides de añadir el permiso de Internet en el AndroidManifest:

<uses-permission android:name="android.permission.INTERNET"/>

Para cargar una imagen, tan solo hay que usar una AsyncImage y pasarle la URL por el parámetro model:

AsyncImage(
    model = "https://loremflickr.com/400/400/cat?lock=1",
    contentDescription = null,
)

El contentDescription es para temas de accesibilidad, y deberían ser un texto que indique lo que muestra la imagen. De momento, y por simplificar, lo vamos a dejar a null.

Tendrás que ejecutar en el dispositivo, ya que las imágenes de red no las carga la preview. El resultado será este:

Esto lo conseguirás si has aplicado todos los pasos de artículos anteriores, y especialmente lo que vimos en el de layouts en Jetpack Compose, y añades la imagen en el Box al que pusimos el fondo rojo. Aquí ya se lo he quitado.

Imagina que quieres transformar la imagen y hacerla circular. Para ello, puedes aplicar una transformation. Necesitarás usar el builder de ImageRequest:

AsyncImage(
    model = ImageRequest.Builder(LocalContext.current)
        .data("https://loremflickr.com/400/400/cat?lock=1")
        .transformations(CircleCropTransformation())
        .build(),
    contentDescription = null,
    modifier = Modifier.clip(CircleShape)
)

Existen alguna transformación más, como RoundedCornersTransformation. Y por supuesto, puedes crearte las tuyas propias.

Se pueden definir muchas más cosas en ese builder, pero por ejemplo una interesante es la transición. Puedes hacer un efecto de crossfade para cuando cargue:

model = ImageRequest.Builder(LocalContext.current)
        .data("https://loremflickr.com/400/400/cat?lock=1")
        .transformations(CircleCropTransformation())
        .crossfade(true)
        .build(),

Pero volviendo a nuestro objetivo inicial, realmente lo que queremos es que la imagen ocupe todo el espacio disponible. el modifier viene a nuestro rescate.

Si tienes dudas sobre cómo funcionan los Modifiers, tengo un artículo entero hablando sobre ello.

AsyncImage(
    model = "https://loremflickr.com/400/400/cat?lock=1",
    contentDescription = null,
    modifier = Modifier.fillMaxSize(),
)

Pero esto no es suficiente, ya que si te fijas, la imagen mantiene unas dimensiones que permitan que se vea entera:

Lo que queremos es que ocupe el espacio disponible. Para ello, tenemos el argumento contentScale, que sería el equivalente al scaleType del sistema de vistas original. Podemos usar el Crop:

AsyncImage(
    model = "https://loremflickr.com/400/400/cat?lock=1",
    contentDescription = null,
    modifier = Modifier.fillMaxSize(),
    contentScale = ContentScale.Crop
)

¡Ya lo tenemos!

Cargar iconos de la librería de Material

Ahora que tenemos esta imagen cargada, necesitamos mostrar un icono encima.

Vamos a representar que tenemos fotos y vídeos, y el vídeo tendrá que llevar un icono de play.

Para ello, vamos a usar los iconos que nos provee Jetpack Compose. El componente que necesitamos usar es Icon:

Icon(
    Icons.Default.PlayArrow,
    contentDescription = null,
    modifier = Modifier.size(92.dp)
)

Estamos cargando un icono de «Play» con un tamaño de 92dp:

Si te fijas, estamos usando Icons.Default. Existen distintos estilos de iconos:

  • Filled (es el Default)
  • Outlined
  • Rounded
  • TwoTone
  • Sharp

Puedes usar el estilo que más te interese para tu app particular.

Nos faltan 3 cosas:

Cambiar el color de un icono

Para ello, el Icon tiene un atributo tint. Vamos a ponerlo blanco:

Icon(
    Icons.Default.PlayArrow,
    contentDescription = null,
    modifier = Modifier.size(92.dp),
    tint = Color.White
)

El icono no se corresponde

La realidad es que en Jetpack Compose solo vienen un número limitado de iconos por defecto. Si quieremos añadir más, tenemos que incluir esta librería:

implementation "androidx.compose.material:material-icons-extended:$compose_version"

Ahora ya podemos seleccionar PlayCircleOutline:

Icon(
    Icons.Default.PlayCircleOutline,
    contentDescription = null,
    modifier = Modifier.size(92.dp),
    tint = Color.White
)

El icono no está centrado

Esto ya hemos visto cómo hacerlo anteriormente, con el modifier align:

Icon(
    Icons.Default.PlayCircleOutline,
    contentDescription = null,
    modifier = Modifier
        .size(92.dp)
        .align(Alignment.Center),
    tint = Color.White
)

Si ahora ejecutamos, tendremos el resultado que buscábamos. Es más ancho que la imagen original, pero esto solo será hasta que podamos ponerlo en un Grid que nos ajuste el ancho a lo necesario:

Cargar iconos desde resourrces

También es posible cargar drawables clásicos con otra sobrecarga del Composable Icon que recibe un painter. También podríamos cargarlo en un Image.

Para ello solo habría que usar este painter:

painter = painterResource(id = R.drawable.my_resource)

Con esto ya tenemos todo lo necesario para cargar las imágenes, los textos, e incluso mostrar el icono de vídeo si toca, pero aún no hemos hablado de cómo proveerle esa información.

Para esto es importante entender cómo funciona el estado, pero antes quiero enseñarte cómo crear una lista de elementos

Si no quieres perderte nada sobre este curso de Jetpack Compose, y además poder acceder a soporte y contenido extra, te animo a que te apuntes a mi formación gratuita:

Apúntate ahora a Compose Expert y accede a más de 3h de contenido gratuito, soporte, extras y mucho más totalmente gratis.

También puedes ver el código en el commit correspondiente del repositorio.

Quizá también te interese…

Temas, colores, tipografías y formas en Jetpack Compose

Temas, colores, tipografías y formas en Jetpack Compose

Si vienes del sistema clásico de vistas, recordarás que toda la definición de temas se hacía de una forma bastante tediosa a través de styles en XML. Si odias tu vida (o estás en una App donde mezclas XMLs y Jetpack Compose), aún puedes seguir usando esos temas...

Usando Cards de Material Design en Jetpack Compose

Usando Cards de Material Design en Jetpack Compose

Tenemos ya una App de lo más resultona después de todos los artículos que hemos visto hasta ahora. Puedes encontrarlos todos bien ordenaditos de forma gratuita en el área privada de Compose Expert. https://youtu.be/iZiXpWRIl3U Pero antes de finalizar nuestro camino,...

Navegación en Jetpack Compose con Navigation Compose

Navegación en Jetpack Compose con Navigation Compose

Jetpack Compose es un cambio de paradigma enorme en muchos aspectos. Cambia la forma de pensar en casi todos los puntos involucrados en el desarrollo de una App Android. Y la navegación no iba a ser menos. ¿Cómo se navega en Jetpack Compose? ¿Qué opciones tenemos?...

0 comentarios

Enviar un comentario

Los datos personales que proporciones a través de este formulario quedarán registrados en un fichero de Antonio Leiva Gordillo, con el fin de gestionar los comentarios que realizas en este blog. La legitimación se realiza a través del consentimiento de la parte interesada. Si no se acepta, no podrás comentar en este blog. Los datos que proporciona solo se utilizan para evitar el correo no deseado y no se usarán para nada más. Puede ejercer los derechos de acceso, rectificación, cancelación y oposición en contacto@devexperto.com.

Tu dirección de correo electrónico no será publicada.

Acepto la política de privacidad *