占位符与错误状态
优雅地处理加载过程和失败情况
占位符与错误状态 是 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 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 Coil 加载图片
- 占位符与错误状态
- 缓存与性能
- 播放音频与视频