Coil - 基于Kotlin协程的Android图片加载库 - 7

1,495 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Jetpack Compose

要支持Jetpack Compose, 需要引入扩展库:

implementation("io.coil-kt:coil-compose:2.2.0")

使用可组合函数AsyncImage来加载并展示图片:

AsyncImage(
    model = "https://example.com/image.jpg",
    contentDescription = null
)

model要么是ImageRequest.data要么是ImageRequest自身. contentDescription设置了无障碍服务使用的文本, 描述了图片表示的东西.

AsyncImage

AsyncImage是一个可组合函数, 用于异步执行图片请求并渲染结果. 它支持和标准Image可组合函数相同的参数, 而且它额外地支持设置placeholder/error/fallback painter 和onLoading/onSuccess/onError 回调. 下面的例子展示了如果在加载图片的时候设置circle crop, crossfade和placeholder:

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

SubcomposeAsyncImage

SubcomposeAsyncImageAsyncImage的变体, 使用subcomposition而非PainterAsyncImagePainter的状态提供API. 写个例子看一下:

SubcomposeAsyncImage(
    model = "https://example.com/image.jpg",
    loading = {
        CircularProgressIndicator()
    },
    contentDescription = stringResource(R.string.description)
)

此外, 还可以通过content参数和SubcomposeAsyncImageContent拥有更复杂的逻辑, 两者是渲染当前状态的:

SubcomposeAsyncImage(
    model = "https://example.com/image.jpg",
    contentDescription = stringResource(R.string.description)
) {
    val state = painter.state
    if (state is AsyncImagePainter.State.Loading || state is AsyncImagePainter.State.Error) {
        CircularProgressIndicator()
    } else {
        SubcomposeAsyncImageContent()
    }
}

Subcomposition要比常规组合性能差一些, 所以在需要高性能的场景下(比如lists), Subcomposition也是不适合这部分UI.

注意

如果使用ImageRequest.Builder.size(比如size(Size.ORIGINAL))为ImageRequest自定义大小, SubcomposeAsyncImage将不会使用subcomposition, 因为它不需要向可组合项提供约束.

AsyncImagePainter

在内部, AsyncImageSubcomposeAsyncImage使用AsyncImagePainter加载model. 如果你需要Painter且不能使用AsyncImage, 你可以使用rememberAsyncImagePainter来加载图片:

val painter = rememberAsyncImagePainter("https://example.com/image.jpg")

rememberAsyncImagePainter是低层级API, 可能在所以场景下不能表现地如预期一样.

注意

如果要在渲染AsyncImagePainterImage上设置自定义ContentScale, 你也应该在rememberAsyncImagePainter里面设置. 决定加载图片的正确维度也很必要.

Observing AsyncImagePainter.state

图片请求需要大小来决定输出图片的维度. 默认情况下, AsyncImageAsyncImagePainter可以决定请求的大小, 但是是在组合发生之后, 且在首帧绘制之前. 使用这种方式决定大小以最大化性能. 这意味着AsyncImagePainter.state在首次组合的时候已经在加载了 - 即使图片已经在内存缓存中存在并且将首帧绘制.

如果需要AsyncImagePainter.state在首次组合期间保持最新, 请使用SubcomposeAsyncImage或者通过ImageRequest.Builder.size为图片请求设置自定义尺寸. 比如, AsyncImagePainter.state在下下面的例子中在首次组合期间将总是保持最新:

val painter = rememberAsyncImagePainter(
    model = ImageRequest.Builder(LocalContext.current)
        .data("https://example.com/image.jpg")
        .size(Size.ORIGINAL) // Set the target size to load the image at.
        .build()
)

if (painter.state is AsyncImagePainter.State.Success) {
    // This will be executed during the first composition if the image is in the memory cache.
}

Image(
    painter = painter,
    contentDescription = stringResource(R.string.description)
)

Transitions

使用ImageRequest.Builder.crossfade将启用内置的渐变转场动画:

AsyncImage(
    model = ImageRequest.Builder(LocalContext.current)
        .data("https://example.com/image.jpg")
        .crossfade(true)
        .build(),
    contentDescription = null
)

自定义Transition对于AsyncImageSubcomposeAsyncImage或者rememberAsyncImagePainter不起作用, 因为它要求有View引用. CrossfadeTransition可以是因为内部特殊的支持.

即使如此, 通过观察AsyncImagePainter的状态在Compose中创建自定义转场动画也是有可能的:

val painter = rememberAsyncImagePainter("https://example.com/image.jpg")

val state = painter.state
if (state is AsyncImagePainter.State.Success && state.dataSource != DataSource.MEMORY_CACHE) {
    // Perform the transition animation.
}

Image(
    painter = painter,
    contentDescription = stringResource(R.string.description)
)