0Pricing
Android Academy · Leçon

Espaces réservés et états d’erreur

Gérez élégamment le chargement et les échecs.

Espaces réservés et états d’erreur est une leçon Android Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Android Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Android Academy comprend 4 leçons au total.

Le chargement n’est pas instantané

Les images provenant du réseau prennent du temps à s’afficher. Pendant ce délai, votre interface n’affiche… rien, à moins que vous ne le gériez. Et parfois, l’image n’arrive jamais : l’URL est incorrecte, le serveur est indisponible ou l’utilisateur est hors ligne.

Une application soignée affiche un espace réservé pendant le chargement et une solution de secours en cas d’échec, plutôt qu’un emplacement vide ou un plantage. Coil facilite ces deux tâches.

Dans cette leçon, vous ajouterez des espaces réservés, des images d’erreur et une interface personnalisée pour chaque état.

Un espace réservé simple

La manière la plus rapide d’ajouter un espace réservé consiste à transmettre un drawable à AsyncImage via le paramètre placeholder, en utilisant painterResource. Coil l’affiche jusqu’à la fin du chargement de l’image réelle.

Utilisez un drawable neutre et léger, comme une boîte grise ou la silhouette d’un logo.

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)
    )
}

Ajouter une image d’erreur

Le paramètre error affiche un drawable de secours lorsque la requête échoue. Il existe également fallback, utilisé spécifiquement lorsque le model est null, c’est-à-dire lorsqu’il n’y a aucune donnée à charger.

En définissant les trois paramètres, placeholder, error et fallback, vous couvrez tous les états visuels avec presque aucun code.

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)
    )
}

Un espace réservé via ImageRequest

Vous pouvez également définir ces états directement dans le ImageRequest. C’est pratique lorsque vous construisez la requête une fois pour la réutiliser, ou lorsque vous devez définir d’autres options de requête en même temps.

Les fonctions définies au niveau de la requête acceptent des identifiants de ressources 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()

Une interface personnalisée avec SubcomposeAsyncImage

Les drawables conviennent dans de nombreux cas, mais vous pouvez parfois vouloir afficher un indicateur, un effet de scintillement ou du texte selon l’état. SubcomposeAsyncImage vous permet de fournir de véritables composables pour les états loading, error et success.

Cette solution est plus flexible, mais légèrement plus lourde. Préférez donc les espaces réservés sous forme de drawable pour les longues listes et réservez la sous-composition aux images 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") }
    )
}

Lire directement l’état

Pour un contrôle total, utilisez l’API rememberAsyncImagePainter et examinez son state. L’état est un type scellé avec les cas Loading, Success, Error et Empty.

Vous pouvez ainsi piloter votre propre mise en page, vos animations ou vos statistiques en fonction du résultat obtenu.

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)
    }
}

Afficher un espace réservé scintillant

Un schéma très courant est l’effet de scintillement : un dégradé gris animé qui suggère que le contenu va bientôt apparaître. Vous pouvez en créer une version simple avec une boîte de couleur unie derrière l’état de chargement.

Ici, nous utilisons SubcomposeAsyncImage avec une boîte teintée pendant le chargement. Remplacez la boîte par une bibliothèque de scintillement si vous souhaitez obtenir l’effet animé.

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)
            )
        }
    )
}

Gérer un modèle null ou vide

Que faire si vous n’avez pas encore d’URL, par exemple pour un utilisateur sans photo de profil ? Transmettre null comme modèle déclenche le drawable fallback, ou error si aucun fallback n’est défini.

Vous évitez ainsi de traiter vous-même le cas null ; laissez Coil afficher un avatar par défaut.

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)
    )
}

Écouter les erreurs avec un listener

Vous devez parfois réagir dans le code lorsqu’un chargement échoue, par exemple pour consigner l’erreur ou réessayer. ImageRequest.Builder accepte un listener avec des rappels de réussite et d’erreur.

Veillez à garder ces rappels légers ; ils s’exécutent sur le thread 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()

Teintes et filtres de couleur

Les drawables d’espace réservé et d’erreur sont souvent plus harmonieux lorsqu’ils sont teintés pour correspondre à votre thème. Vous pouvez appliquer un ColorFilter à un AsyncImage, ce qui convient parfaitement aux solutions de secours monochromes de type icône qui doivent s’adapter aux modes clair et sombre.

Appliquez le filtre uniquement à l’apparence d’espace réservé ou d’erreur souhaitée ; pour les vraies photos, vous le laissez généralement désactivé.

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 composant d’image résilient

Assemblons un composant réutilisable qui gère proprement chaque état : un arrière-plan de chargement, une solution de secours en cas d’erreur, un fondu et un recadrage. Ajoutez-le à une liste et vous n’aurez plus à vous soucier des emplacements vides.

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)
    )
}

Vérification rapide

Vous transmettez model = null à AsyncImage. Quel drawable Coil affiche-t-il par défaut ?

Récapitulatif

Vous avez rendu le chargement des images plus fiable. Points essentiels :

  • placeholder s'affiche pendant le chargement, error en cas d'échec et fallback lorsque le modèle est nul.
  • Définissez-les sur AsyncImage ou sur ImageRequest.
  • SubcomposeAsyncImage vous permet de fournir de véritables composables (indicateur de chargement, effet de scintillement) pour chaque état.
  • rememberAsyncImagePainter expose le state brut pour un contrôle total.
  • Utilisez un listener de requête pour enregistrer les résultats du chargement ou y réagir.

Ensuite : la mise en cache et les performances, pour que les images se chargent instantanément la deuxième fois.

Questions Fréquemment Posées

La leçon « Espaces réservés et états d’erreur » est-elle gratuite ?

Oui — le texte complet de « Espaces réservés et états d’erreur » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Android Academy, passe à CoddyKit PRO. Le cours Android Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Espaces réservés et états d’erreur » ?

Gérez élégamment le chargement et les échecs. Tu pratiques Android Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Android Academy ?

Aucune expérience préalable n'est requise. Android Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.

Combien de temps prend la leçon « Espaces réservés et états d’erreur » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Android Academy ?

Oui. Chaque leçon Android Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Charger des images avec Coil
  2. Espaces réservés et états d’erreur
  3. Mise en cache et performances
  4. Lire des fichiers audio et vidéo
← Retour à Android Academy