Compose
要添加对 Compose UI 的支持,请导入扩展库:
implementation("io.coil-kt.coil3:coil-compose:3.3.0")
然后使用 AsyncImage
可组合函数 (composable) 来加载并显示图片:
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = null,
)
model
可以是 ImageRequest.data
值,也可以是 ImageRequest
本身。contentDescription
设置了辅助功能服务用于描述此图片所代表内容的文本。
AsyncImage
AsyncImage
是一个可组合函数,它异步执行图片请求并渲染结果。它支持与标准 Image
可组合函数相同的参数,此外,它还支持设置 placeholder
/error
/fallback
绘制器 (painter) 和 onLoading
/onSuccess
/onError
回调。这是一个加载带圆形裁剪、交叉渐变并设置占位符的图片示例:
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data("https://example.com/image.jpg")
.crossfade(true)
.build(),
placeholder = painterResource(R.drawable.placeholder),
contentDescription = stringResource(R.string.description),
contentScale = ContentScale.Crop,
modifier = Modifier.clip(CircleShape),
)
何时使用此函数:
在大多数情况下,首选使用 AsyncImage
。它会根据可组合函数的约束和提供的 ContentScale
,正确确定图片应加载的大小。
rememberAsyncImagePainter
在内部,AsyncImage
和 SubcomposeAsyncImage
使用 rememberAsyncImagePainter
来加载 model
。如果你需要的是 Painter
而不是可组合函数,你可以使用 rememberAsyncImagePainter
加载图片:
val painter = rememberAsyncImagePainter("https://example.com/image.jpg")
rememberAsyncImagePainter
比 AsyncImage
和 SubcomposeAsyncImage
更灵活,但有几个缺点(见下文)。
何时使用此函数:
如果你需要 Painter
而不是可组合函数,或者如果你需要观察 AsyncImagePainter.state
并根据其绘制不同的可组合函数,或者如果你需要使用 AsyncImagePainter.restart
手动重新启动图片请求,此函数会很有用。
此函数的主要缺点是它无法检测图片在屏幕上加载时的大小,并且总是以其原始尺寸加载图片。你可以传递自定义的 SizeResolver
或使用 rememberConstraintsSizeResolver
(AsyncImage
内部使用的就是它)来解决此问题。示例:
val sizeResolver = rememberConstraintsSizeResolver()
val painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalPlatformContext.current)
.data("https://example.com/image.jpg")
.size(sizeResolver)
.build(),
)
Image(
painter = painter,
contentDescription = null,
modifier = Modifier.then(sizeResolver),
)
另一个缺点是,当使用 rememberAsyncImagePainter
时,AsyncImagePainter.state
在首次组合 (composition) 时将始终为 AsyncImagePainter.State.Empty
,即使图片存在于内存缓存中并将在第一帧中绘制。
SubcomposeAsyncImage
SubcomposeAsyncImage
是 AsyncImage
的一个变体,它使用子组合 (subcomposition) 为 AsyncImagePainter
的状态提供槽位 API (slot API),而不是使用 Painter
。这是一个示例:
SubcomposeAsyncImage(
model = "https://example.com/image.jpg",
loading = {
CircularProgressIndicator()
},
contentDescription = stringResource(R.string.description),
)
此外,你可以使用其 content
参数和 SubcomposeAsyncImageContent
实现更复杂的逻辑,SubcomposeAsyncImageContent
会渲染当前状态:
SubcomposeAsyncImage(
model = "https://example.com/image.jpg",
contentDescription = stringResource(R.string.description)
) {
val state by painter.state.collectAsState()
if (state is AsyncImagePainter.State.Success) {
SubcomposeAsyncImageContent()
} else {
CircularProgressIndicator()
}
}
::: Note 子组合 (subcomposition) 比常规组合 (composition) 慢,因此此可组合函数可能不适用于 UI 中对性能要求较高的部分(例如 LazyList
)。
::: 何时使用此函数:
通常,如果你需要观察 AsyncImagePainter.state
,请首选使用 rememberAsyncImagePainter
而不是此函数,因为它不使用子组合。
具体而言,此函数仅在你需要观察 AsyncImagePainter.state
且它不能像 rememberAsyncImagePainter
那样在首次组合和第一帧中为 Empty
时才有用。SubcomposeAsyncImage
使用子组合来获取图片的约束,因此其 AsyncImagePainter.state
会立即更新。
观察 AsyncImagePainter.state
val painter = rememberAsyncImagePainter("https://example.com/image.jpg")
val state by painter.state.collectAsState()
when (state) {
is AsyncImagePainter.State.Empty,
is AsyncImagePainter.State.Loading -> {
CircularProgressIndicator()
}
is AsyncImagePainter.State.Success -> {
Image(
painter = painter,
contentDescription = stringResource(R.string.description)
)
}
is AsyncImagePainter.State.Error -> {
// Show some error UI.
}
}
过渡
你可以使用 ImageRequest.Builder.crossfade
启用内置的交叉渐变过渡:
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data("https://example.com/image.jpg")
.crossfade(true)
.build(),
contentDescription = null,
)
自定义 Transition
不适用于 AsyncImage
、SubcomposeAsyncImage
或 rememberAsyncImagePainter
,因为它们需要 View
引用。CrossfadeTransition
由于特殊的内部支持而起作用。
也就是说,可以通过观察 AsyncImagePainter.state
在 Compose 中创建自定义过渡:
val painter = rememberAsyncImagePainter("https://example.com/image.jpg")
val state by painter.state.collectAsState()
if (state is AsyncImagePainter.State.Success && state.result.dataSource != DataSource.MEMORY_CACHE) {
// Perform the transition animation.
}
Image(
painter = painter,
contentDescription = stringResource(R.string.description),
)
预览
AsyncImage
/rememberAsyncImagePainter
/SubcomposeAsyncImage
在 Android Studio 预览中的行为由 LocalAsyncImagePreviewHandler
控制。默认情况下,它会在预览环境中尝试正常执行请求。预览环境中网络访问被禁用,因此网络 URL 总是会失败。
你可以这样覆盖预览行为:
val previewHandler = AsyncImagePreviewHandler {
ColorImage(Color.Red.toArgb())
}
CompositionLocalProvider(LocalAsyncImagePreviewHandler provides previewHandler) {
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = null,
)
}
这对于 AndroidX 的 Compose 预览截图测试库也很有用,该库在相同的预览环境中执行。
Compose Multiplatform 资源
Coil 支持使用 Res.getUri
作为 model
参数来加载 Compose Multiplatform 资源。示例:
AsyncImage(
model = Res.getUri("drawable/sample.jpg"),
contentDescription = null,
)
::: Note Coil 不支持 Res.drawable.image
和其他编译安全引用。你必须改用 Res.getUri("drawable/image")
。 关注此 issue 获取更新。
:::