Compose 图片加载新姿势 — Coil 新手基础教程

如果你在 Compose 下开发应用,那么图片加载库,一定会用 Coil

本文将介绍 Coil 的基础用法,手把手带你掌握 Coil 在 Compose 中的应用,让图片加载简单高效。

来者何人

Coil 是适用于 Android 和 Compose Multiplatform 的图像加载库。它具有以下特点:

  • 快速:Coil 执行多项优化,包括内存和磁盘缓存、图像降采样、自动暂停/取消请求等。
  • 轻量:Coil 仅依赖于 Kotlin、Coroutines 和 Okio,可与 Google 的 R8 代码压缩器无缝协作。
  • 易于使用:Coil 的 API 利用 Kotlin 的语言功能实现简单性并减少样板代码。
  • 现代:Coil 是 Kotlin 优先的,可与 Compose、Coroutines、OkioOkHttpKtor 等现代库互操作。

为什么是 Coil 这个单词?

Co routine I mage Loader。

在正式开始讲解之前,我们会用到一张图片:

一张 1920 x 1080 的图片

快速入门

Kotlin 复制代码
const val PIC_URL = "https://images.unsplash.com/photo-1604156788856-2ce5f2171cce?fm=jpg&q=60&w=1920"

AsyncImage(
    model = PIC_URL,
    contentDescription = "ballon"
)

是的,基本上你不用任何配置,在 Compose 中,直接使用 AsyncImage 就可以加载一张图片,PIC_URL 指向一张在线图片地址。

在平板中,显示效果是这样的:

如果你什么都不设置,AsyncImage 会自动使用原图大小直到占满整个布局。

引入依赖

OK,虽然我们已经学会了如何使用 AsyncImage,但是,我还没有告诉你如何引入依赖呢。

如果你是初学者,我建议你直接使用 bom 配置:

Kotlin 复制代码
val coilBom = platform("io.coil-kt.coil3:coil-bom:3.3.0")
implementation(coilBom)
implementation("io.coil-kt.coil3:coil-compose")
implementation("io.coil-kt.coil3:coil-network-okhttp")

coil-bom 会帮助你进行版本管理,在后续使用 Coil 其他组件的时候,我们只需要生命组件的名称,而无需特定声明版本。

compose-bom 也是这么工作的。

好的,在引入简单的依赖之后,上述代码,便可以运行,你可以尝试运行在自己的机器上看看效果。

AyncImage

AsyncImage 是一个可组合函数,它异步执行图像请求并渲染结果。

它的用法和 Compose 的 Image 几乎是一样的。

接下来我们展示一下 AsyncImage 的一些基本用法。为了让效果看起来更明显,我们会给 AsyncImage 一个默认的黑色背景:

Kotlin 复制代码
AsyncImage(
    modifier = Modifier.size(400.dp).background(Color.Black), // 黑色背景表示控件区域
    model = PIC_URL,
    contentScale = ContentScale.Crop,
    contentDescription = "ballon"
)

这里使用了一个 contentScale 参数。

ContentScale 表示一种规则,他表示一种对源图片进行缩放,使其显示在目标矩形的一种规则。

此处我们使用了 ContentScale.Crop,它会均匀的对源图片进行缩放,当其一条边满足目标矩形的时候,便停止。

我们看下效果:

你会发现,源图片优先满足了高,然后居中显示,对长进行了裁剪。它类似于 android.widget.ImageView.ScaleType.CENTER_CROP

如果我们想对裁剪的方向进行控制,可以结合 alignment

Kotlin 复制代码
AsyncImage(
    modifier = Modifier.size(400.dp).background(Color.Black),
    model = PIC_URL,
    contentScale = ContentScale.Crop,
    alignment = Alignment.CenterStart, // 从左中开始裁剪
    contentDescription = "ballon"
)

对比原图,这一次确实是从头开始裁剪,并不是居中裁剪了。

如果你想整体居中显示:

Kotlin 复制代码
AsyncImage(
    modifier = Modifier.size(400.dp).background(Color.Black),
    model = PIC_URL,
    contentScale = ContentScale.Inside,
    contentDescription = "ballon"
)

ContentScale.Inside 正是你想要的:

注意黑色区域,证明没有铺满整个 AsyncImage,达到了居中显示的效果

当我们配合 Alignment.BottomCenter 控制显示区域:

占位图

网络图片是有加载过程的,所以,很多图片加载库会提供占位图来提升用户体验,Coil 也不例外。

Coil 提供三种占位图,分别是加载中,失败,以及空数据:

Kotlin 复制代码
AsyncImage(
    modifier = Modifier.size(400.dp).background(Color.Black),
    model = PIC_URL,
    contentScale = ContentScale.Crop,
    error = painterResource(R.drawable.icn_failed), // 失败占位图
    placeholder = painterResource(R.drawable.icn_loading),// 加载占位图
    fallback = painterResource(R.drawable.icn_null), // 空数据占位图
    contentDescription = "ballon"
)

加载占位图会在下载网络图片的时候显示,失败占位图会在图片下载失败的时候展示,而空数据占位图,则在参数 modelnull 的时候展示。

我将使用如下三张图片分别作为失败,加载中,空数据的占位图,每张图片均是 128 像素的大小。

当源图片加载中的时候:

实际上这时候的效果是有问题的,你会发现加载占位图太大了,占满了整个区域,没关系,一会儿解决。

如果网络有问题,或者给的图片的 URL 有问题,则会显示失败的占位图:

如果 modelnull

Kotlin 复制代码
//...
model = null,
//...

则显示空数据占位图。

好的,现在我们修正一下显示效果。

正如我在记载占位图提到的,这个效果是有问题的,因为一般 UI 提供的占位图是比较小的,它的 contentScale 参数不能和源图片一样,所以这里我们要动态调整一下 contentScale

幸好,AsyncImage 已经做好了这一切。

AsyncImage 支持加载相关的一系列回调,

Kotlin 复制代码
//...
onLoading: ((State.Loading) -> Unit)? = null, // 加载中
onSuccess: ((State.Success) -> Unit)? = null, // 成功
onError: ((State.Error) -> Unit)? = null,     // 失败
//...

我们使用这些回调,动态的修改 contentScale

Kotlin 复制代码
var succeed by remember { mutableStateOf(false) } // 图片加载是否成功

AsyncImage(
    modifier = Modifier.size(400.dp).background(Color.Black),
    model = PIC_URL,
    contentScale = if (succeed) ContentScale.Crop else ContentScale.Inside, // 调整
    error = painterResource(R.drawable.icn_failed),
    placeholder = painterResource(R.drawable.icn_loading),
    fallback = painterResource(R.drawable.icn_null),
    onLoading = {
        succeed = false // 接收回调
    },
    onError = {
        succeed = false // 接收回调
    },
    onSuccess = {
        succeed = true // 接收回调
    },
    contentDescription = "ballon"
)

好的,我们来看下效果:

未完待续

如果你只是想简单使用 Coil,那么本篇文章教你的这些,已经够用了,甚至很多大型项目,基本也就这么用。

但如果你想更深入了解 Coil,想知道内存优化、视频封面加载、占位图动效等怎么做,那么下一篇文章,你一定要看!

相关推荐
夏天的味道٥2 小时前
MySQL explain命令的作用
android·mysql·adb
鹏多多2 小时前
flutter-使用confetti制作炫酷纸屑爆炸粒子动画
android·前端·flutter
雨白14 小时前
Drawable 与 Bitmap 的区别、互转与自定义
android
程序员江同学14 小时前
Kotlin 技术月报 | 2025 年 8 月
android·kotlin
nju永远得不到的男人14 小时前
关于virtual camera
android
雨白17 小时前
Android 自定义 View:属性动画和硬件加速
android
hellokai18 小时前
React Native新架构源码分析
android·前端·react native
真西西18 小时前
Koin:Kotlin轻量级依赖注入框架
android·kotlin
CYRUS_STUDIO20 小时前
手把手教你改造 AAR:解包、注入逻辑、重打包,一条龙玩转第三方 SDK!
android·逆向