0Pricing
Android Academy · 课时

占位符与错误状态

优雅地处理加载过程和失败情况

占位符与错误状态 是 CoddyKit 上的免费 Android Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 4 节课。

加载并非瞬间完成

网络图片需要一定时间才能加载。在这段等待期间,如果您不进行处理,界面就只能显示……空白。有时图片甚至永远不会到达:URL 已失效、服务器停机,或者用户处于离线状态。

精心设计的应用会在加载时显示占位图,在发生问题时显示备用内容,而不是留下空洞或直接崩溃。Coil 让这两件事都变得很简单。

本课中,您将添加占位图、错误图片以及按状态定制的界面。

简单的占位图

添加占位图最快的方法,是通过 painterResource 将一个可绘制对象作为 placeholder 参数传给 AsyncImage。在真实图片加载完成前,Coil 会一直显示它。

请使用中性且轻量的可绘制对象,例如灰色方块或徽标轮廓。

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

添加错误图片

请求失败时,error 参数会显示一个备用可绘制对象。还有 fallback,专门用于 model 为 null 的情况(完全没有可加载的数据)。

同时设置 placeholder、error 和 fallback,几乎无需编写额外代码,就能覆盖所有视觉状态。

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

通过 ImageRequest 设置占位图

您也可以直接在 ImageRequest 上设置这些状态。当您需要构建一次请求并重复使用,或同时需要设置其他请求选项时,这种方式非常方便。

请求级别的函数接收可绘制资源 ID。

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

使用 SubcomposeAsyncImage 自定义界面

可绘制对象已经够用,但有时您可能希望为每种状态显示加载指示器、闪烁效果或文字。SubcomposeAsyncImage 允许您为 loading、error 和 success 提供真正的可组合内容。

它更加灵活,但开销也略高,因此长列表中应优先使用可绘制占位图,将子组合方式留给主视觉图片。

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

直接读取状态

如果需要完全控制,请使用 rememberAsyncImagePainter API 并检查其 state。该状态是一个密封类型,包含 Loading、Success、Error 和 Empty 几种情况。

这样,您就可以根据加载结果驱动自己的布局、动画或分析功能。

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

显示闪烁占位图

一种常见模式是闪烁效果:用动态灰色渐变暗示内容即将出现。您可以在加载状态后面放置纯色方块,构建一个简单版本。

这里使用 SubcomposeAsyncImage,在加载期间显示带色调的方块。如果需要动态效果,可以将方块替换为闪烁效果库。

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

处理空或无效的模型

如果您暂时没有 URL,例如用户没有头像,该怎么办?将 null 作为模型传入,会触发 fallback 可绘制对象(如果未设置 fallback,则触发 error)。

这样可以避免在自己的代码中特别处理 null,让 Coil 显示默认头像即可。

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

使用 listener 监听错误

有时您需要在加载失败时执行一些代码,例如记录信息或重试。ImageRequest.Builder 接受带有成功和错误回调的 listener。

请让这些回调保持轻量,因为它们运行在主线程上。

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

着色与颜色滤镜

占位图和错误图通常经过着色后会更符合主题。您可以将 ColorFilter 应用于 AsyncImage,这非常适合会随浅色和深色模式变化的单色图标式备用内容。

只对您希望改变外观的占位图或错误图应用滤镜;对于真实照片,通常应关闭滤镜。

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

健壮的图片组件

让我们组装一个可复用的组件,整洁地处理所有状态:加载背景、错误备用内容、淡入效果和裁剪。将它放入列表后,就再也不用担心出现空白区域了。

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

快速检查

您将 model = null 传递给了 AsyncImage。Coil 默认会显示哪个可绘制对象?

回顾

您已经实现了更加平滑的图片加载体验。要点如下:

  • 加载时显示 placeholder,加载失败时显示 error,模型为 null 时显示 fallback。
  • 您可以在 AsyncImage 或 ImageRequest 上设置它们。
  • SubcomposeAsyncImage 允许您为每种状态提供真正的可组合项(加载指示器、闪烁效果等)。
  • rememberAsyncImagePainter 会公开原始的 state,让您获得完全的控制权。
  • 使用请求的 listener 来记录加载结果或对其作出响应。

下一步:学习缓存和性能优化,让图片在第二次加载时瞬间显示。

常见问题解答

「占位符与错误状态」课时是免费的吗?

是的 — 「占位符与错误状态」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 4 节课。

「占位符与错误状态」这节课中我会学到什么?

优雅地处理加载过程和失败情况 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Android Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「占位符与错误状态」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Android Academy 课中编写并运行代码吗?

能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 Coil 加载图片
  2. 占位符与错误状态
  3. 缓存与性能
  4. 播放音频与视频
← 返回 Android Academy