Marcadores de posición y estados de error
Gestione con elegancia la carga y los errores
Marcadores de posición y estados de error es una lección gratuita de Android Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Android Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Android Academy incluye 4 lecciones en total.
La carga no es instantánea
Las imágenes de red tardan un tiempo. Durante ese intervalo, la interfaz no muestra... nada, a menos que lo gestione. Y a veces la imagen nunca llega: la URL no es válida, el servidor no funciona o el usuario está sin conexión.
Una aplicación cuidada muestra un marcador de posición mientras carga y una alternativa cuando se produce un error, en lugar de dejar un espacio vacío o bloquearse. Coil facilita ambas cosas.
En esta lección añadirá marcadores de posición, imágenes de error y una interfaz personalizada para cada estado.
Un marcador de posición sencillo
La forma más rápida de añadir un marcador de posición es pasar un drawable a AsyncImage mediante el parámetro placeholder y usar painterResource. Coil lo muestra hasta que la imagen real termina de cargarse.
Use un drawable neutro y ligero, como un cuadro gris o la silueta de un logotipo.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun ImageWithPlaceholder(url: String) {
AsyncImage(
model = url,
contentDescription = null,
placeholder = painterResource(R.drawable.placeholder_gray)
)
}Añadir una imagen de error
El parámetro error muestra un drawable alternativo cuando falla la solicitud. También existe fallback, que se usa específicamente cuando model es null (no hay datos que cargar).
Configurar los tres valores —placeholder, error y fallback— cubre todos los estados visuales prácticamente sin código.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun RobustImage(url: String?) {
AsyncImage(
model = url,
contentDescription = null,
placeholder = painterResource(R.drawable.placeholder_gray),
error = painterResource(R.drawable.image_broken),
fallback = painterResource(R.drawable.no_image)
)
}Marcador de posición mediante ImageRequest
También puede establecer estos estados en el propio ImageRequest. Resulta útil cuando crea la solicitud una vez y la reutiliza, o cuando necesita configurar otras opciones de la solicitud al mismo tiempo.
Las funciones de nivel de solicitud reciben identificadores de recursos drawable.
import android.content.Context
import coil3.request.ImageRequest
import coil3.request.crossfade
import coil3.request.error
import coil3.request.placeholder
fun buildRequest(context: Context, url: String) =
ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.placeholder(R.drawable.placeholder_gray)
.error(R.drawable.image_broken)
.build()Interfaz personalizada con SubcomposeAsyncImage
Los drawables son adecuados, pero a veces querrá mostrar un indicador de carga, un efecto de brillo o texto para cada estado. SubcomposeAsyncImage permite proporcionar composables reales para loading, error y success.
Es más flexible, pero también ligeramente más pesado; por eso, prefiera marcadores de posición drawable para listas largas y reserve subcomposición para imágenes principales.
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import coil3.compose.SubcomposeAsyncImage
@Composable
fun HeroImage(url: String) {
SubcomposeAsyncImage(
model = url,
contentDescription = "Hero",
loading = { CircularProgressIndicator() },
error = { Text("Could not load image") }
)
}Leer el estado directamente
Para tener un control total, use la API rememberAsyncImagePainter e inspeccione su state. El estado es un tipo sellado con los casos Loading, Success, Error y Empty.
Esto le permite controlar su propio diseño, las animaciones o las analíticas según lo ocurrido.
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import coil3.compose.AsyncImagePainter
import coil3.compose.rememberAsyncImagePainter
@Composable
fun StateAwareImage(url: String) {
val painter = rememberAsyncImagePainter(model = url)
val state = painter.state.collectAsState().value
when (state) {
is AsyncImagePainter.State.Loading -> { /* show spinner */ }
is AsyncImagePainter.State.Error -> { /* show error UI */ }
else -> Image(painter = painter, contentDescription = null)
}
}Mostrar un marcador de posición con efecto de brillo
Un patrón popular es el efecto de brillo: un degradado gris animado que sugiere que el contenido está a punto de aparecer. Puede crear una versión sencilla con un cuadro de color sólido detrás del estado de carga.
Aquí usamos SubcomposeAsyncImage con un cuadro tintado durante la carga. Sustituya el cuadro por una biblioteca de efectos de brillo si desea la versión animada.
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import coil3.compose.SubcomposeAsyncImage
@Composable
fun ShimmerImage(url: String) {
SubcomposeAsyncImage(
model = url,
contentDescription = null,
loading = {
androidx.compose.foundation.layout.Box(
Modifier.fillMaxSize()
.background(MaterialTheme.colorScheme.surfaceVariant)
)
}
)
}Gestionar un modelo nulo o vacío
¿Qué ocurre si todavía no tiene una URL, por ejemplo, porque un usuario no tiene foto de perfil? Pasar null como modelo activa el drawable fallback (o error si no se ha establecido ningún fallback).
Así evita tener que gestionar null de forma especial en su propio código; deje que Coil muestre un avatar predeterminado.
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun ProfilePicture(photoUrl: String?) {
// If photoUrl is null, Coil shows the fallback default avatar.
AsyncImage(
model = photoUrl,
contentDescription = "Profile picture",
fallback = painterResource(R.drawable.default_avatar),
error = painterResource(R.drawable.default_avatar)
)
}Escuchar errores con un listener
A veces necesita reaccionar mediante código cuando falla una carga, por ejemplo, para registrarlo o reintentarlo. ImageRequest.Builder acepta un listener con callbacks de éxito y error.
Mantenga estos callbacks ligeros; se ejecutan en el subproceso principal.
import android.content.Context
import android.util.Log
import coil3.request.ImageRequest
fun loggedRequest(context: Context, url: String) =
ImageRequest.Builder(context)
.data(url)
.listener(
onError = { _, result ->
Log.e("Coil", "Load failed", result.throwable)
},
onSuccess = { _, _ ->
Log.d("Coil", "Loaded $url")
}
)
.build()Aplicar tintes y filtros de color
Los drawables de marcador de posición y de error suelen verse mejor con un tinte que coincida con el tema. Puede aplicar un ColorFilter a un AsyncImage, lo que resulta ideal para alternativas monocromáticas con estilo de icono que deben adaptarse a los modos claro y oscuro.
Aplique el filtro solo al marcador de posición o al estado de error que desee; normalmente no debe aplicarlo a las fotografías reales.
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun TintedFallback(url: String?) {
AsyncImage(
model = url,
contentDescription = null,
error = painterResource(R.drawable.ic_image_placeholder),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant)
)
}Un componente de imagen resistente
Reunamos todo en un componente reutilizable que gestione correctamente cada estado: un fondo de carga, una alternativa para errores, una transición gradual y un recorte. Añádalo a una lista y olvídese de los espacios vacíos.
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade
@Composable
fun ResilientImage(url: String?, size: Int = 96) {
val context = LocalContext.current
AsyncImage(
model = ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
placeholder = painterResource(R.drawable.placeholder_gray),
error = painterResource(R.drawable.image_broken),
fallback = painterResource(R.drawable.default_avatar),
modifier = Modifier.size(size.dp)
)
}Comprobación rápida
Pasa model = null a AsyncImage. ¿Qué drawable muestra Coil de forma predeterminada?
Resumen
Ha conseguido que la carga de imágenes sea más robusta. Puntos clave:
placeholderse muestra durante la carga,errorcuando falla yfallbackcuando el modelo es nulo.- Puede configurarlos en
AsyncImageo enImageRequest. SubcomposeAsyncImagepermite proporcionar composables reales (indicador de carga, efecto shimmer) para cada estado.rememberAsyncImagePainterexpone elstatesin procesar para tener un control total.- Use un
listenerde la solicitud para registrar los resultados de carga o reaccionar ante ellos.
A continuación: almacenamiento en caché y rendimiento, para que las imágenes se carguen al instante la segunda vez.
Aprende Kotlin con un tutor de IA — gratis
Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.
- Cursos
- 36
- Lecciones
- 152
Preguntas frecuentes
¿La lección «Marcadores de posición y estados de error» es gratis?
Sí — el texto completo de «Marcadores de posición y estados de error» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Android Academy, actualiza a CoddyKit PRO. El curso de Android Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Marcadores de posición y estados de error»?
Gestione con elegancia la carga y los errores Practicas Android Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Android Academy?
No se requiere experiencia previa. Android Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Marcadores de posición y estados de error»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Android Academy?
Sí. Cada lección de Android Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Carga de imágenes con Coil
- Marcadores de posición y estados de error
- Caché y rendimiento
- Reproducción de audio y vídeo