0Pricing
Android Academy · 课时

使用 Coil 加载图片

在 Compose 中异步加载图片

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

为什么需要图像加载器

大多数现代应用都会显示来自互联网的图片:头像、商品图片和文章缩略图。正确下载并解码这些图片,比看起来要困难得多。

您需要在后台线程中通过网络获取字节,将其解码为位图,进行缓存以避免重复获取,并在用户滚动离开时取消请求。手动完成这些工作很容易出错。

Coil(Coroutine Image Loader,协程图像加载器)通过简洁且适合 Compose 的 API 为您处理这一切。本课中,您将加载第一张远程图片。

将 Coil 添加到项目中

Coil 以 Gradle 依赖项的形式提供。对于 Jetpack Compose,您需要使用 coil-compose 构件,它会提供 AsyncImage 可组合项。

将其添加到模块的 build.gradle.kts 中。Coil 3 是当前的主要版本,支持多种平台;本课使用的是它的 Compose 集成。

// build.gradle.kts (module level)
dependencies {
    implementation("io.coil-kt.coil3:coil-compose:3.0.4")
    implementation("io.coil-kt.coil3:coil-network-okhttp:3.0.4")
}

互联网权限

要从 URL 下载图片,您的应用需要 INTERNET 权限。这是一项普通的(安装时授予的)权限,因此只需在清单中声明即可,不会弹出运行时提示。

没有该权限时,每个网络请求都会因安全异常而失败。

<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <application ...>
        ...
    </application>
</manifest>

您的第一个 AsyncImage

在 Compose 中显示远程图片的最简单方式是使用 AsyncImage。为它提供一个 model(通常是 URL 字符串)和一个 contentDescription,剩下的工作由 Coil 完成:后台获取、解码和显示。

它会自动使用与组合绑定的协程,因此当可组合项离开屏幕时,请求也会被取消。

import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage

@Composable
fun Avatar() {
    AsyncImage(
        model = "https://example.com/avatar.png",
        contentDescription = "User avatar",
        modifier = Modifier.size(96.dp)
    )
}

contentScale:图片如何适配

图片很少会与布局槽位的确切尺寸完全匹配。ContentScale 用于控制位图如何缩放到其边界内。

  • ContentScale.Crop 会填满容器并裁剪溢出部分(非常适合头像和缩略图)。
  • ContentScale.Fit 会保留完整图片,但可能留下空白区域。
  • ContentScale.FillBounds 会拉伸图片以填满边界(可能造成变形)。

对于固定尺寸的槽位,最常用的选择是 Crop。

import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage

@Composable
fun CircleAvatar(url: String) {
    AsyncImage(
        model = url,
        contentDescription = "Avatar",
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .size(80.dp)
            .clip(CircleShape)
    )
}

使用 ImageRequest 构建请求

传入 URL 字符串是快捷方式。如果需要更多控制,可以构建一个 ImageRequest。这样您就能设置淡入、请求头、变换和缓存键等选项。

您需要一个 Context,Compose 会通过 LocalContext.current 提供它。

import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade

@Composable
fun ProductImage(url: String) {
    val context = LocalContext.current
    val request = ImageRequest.Builder(context)
        .data(url)
        .crossfade(true)
        .build()

    AsyncImage(
        model = request,
        contentDescription = "Product photo"
    )
}

使用淡入实现平滑加载

默认情况下,图片加载完成后会立即显示,可能显得突兀。crossfade(true) 会让图片在一段很短的动画中逐渐淡入。

您也可以传入以毫秒为单位的时长,让淡入速度变慢或变快。这个小细节能让列表的观感精致许多。

import coil3.request.ImageRequest
import coil3.request.crossfade
import android.content.Context

fun smoothRequest(context: Context, url: String) =
    ImageRequest.Builder(context)
        .data(url)
        .crossfade(durationMillis = 300)
        .build()

从不同来源加载

AsyncImage 中的 model 非常灵活。Coil 可以从多种来源加载图片,而不仅仅是远程 URL:

  • 用于网络或本地文件的 String 或 Uri。
  • 可绘制资源 ID(R.drawable.logo)。
  • 设备中的 File。
  • 来自照片选择器的内容 Uri。

这意味着同一个可组合项可以无缝处理远程图片和本地图片。

import android.net.Uri
import androidx.compose.runtime.Composable
import coil3.compose.AsyncImage

@Composable
fun FlexibleImage(source: Any) {
    // source can be a URL String, a Uri, a File, or a resource id
    AsyncImage(
        model = source,
        contentDescription = null
    )
}

// Examples:
// FlexibleImage("https://example.com/pic.jpg")
// FlexibleImage(Uri.parse("content://media/external/images/1"))

响应加载状态

有时您需要知道图片正在加载、加载成功还是加载失败,以便显示不同的界面。AsyncImagePainter 会公开该状态,但最简单的方式是使用 SubcomposeAsyncImage,它允许您为每种状态提供不同的内容。

下一课我们会深入介绍占位图;这里先了解感知状态的加载结构。

import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import coil3.compose.SubcomposeAsyncImage

@Composable
fun StatefulImage(url: String) {
    SubcomposeAsyncImage(
        model = url,
        contentDescription = "Photo",
        loading = { CircularProgressIndicator() }
    )
}

自动取消请求

Coil 的一大优势是能够自动取消请求。由于 AsyncImage 会在组合中启动请求,当可组合项离开屏幕时(例如某一行滚出 LazyColumn),正在进行的请求就会被取消。

这样可以节省带宽和 CPU,避免执行无用工作。您无需亲自编写任何取消代码,这正是 Coil 能够很好地扩展到长列表的原因。

import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage

@Composable
fun PhotoFeed(urls: List<String>) {
    LazyColumn {
        items(urls) { url ->
            AsyncImage(
                model = url,
                contentDescription = null,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
            )
        }
    }
}

完整的图片卡片

让我们综合所学内容:创建一张卡片,让远程图片填满顶部区域,使用淡入效果,并裁剪到固定高度。这是您会反复复用的基础模式。

import androidx.compose.foundation.layout.*
import androidx.compose.material3.Card
import androidx.compose.material3.Text
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.unit.dp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade

@Composable
fun ImageCard(title: String, imageUrl: String) {
    val context = LocalContext.current
    Card(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
        Column {
            AsyncImage(
                model = ImageRequest.Builder(context)
                    .data(imageUrl)
                    .crossfade(true)
                    .build(),
                contentDescription = title,
                contentScale = ContentScale.Crop,
                modifier = Modifier.fillMaxWidth().height(180.dp)
            )
            Text(text = title, modifier = Modifier.padding(16.dp))
        }
    }
}

快速检查

在 Jetpack Compose 中,coil-compose 提供的哪个可组合项是通过 URL 显示远程图片的最简单方式?

回顾

您已经使用 Coil 加载了第一批远程图片。要点如下:

  • 添加 coil-compose 和 OkHttp 网络依赖项,并添加 INTERNET 权限。
  • AsyncImage(model = url, contentDescription = ...) 是快捷方式。
  • 对于头像和缩略图,使用固定尺寸配合 ContentScale.Crop。
  • 构建 ImageRequest,以实现淡入效果和更多控制。
  • 当可组合项离开屏幕时,Coil 会自动取消正在进行的请求。

下一步:使用占位图和错误状态,让加载过程更自然。

常见问题解答

「使用 Coil 加载图片」课时是免费的吗?

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

「使用 Coil 加载图片」这节课中我会学到什么?

在 Compose 中异步加载图片 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Android Academy 需要有经验吗?

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

「使用 Coil 加载图片」课时需要多长时间?

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

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

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

此课程中的所有课时

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