4. Image 组件与资源加载

在 Jetpack Compose 中,Image 组件是展示图片的核心控件,它支持本地资源、网络图片以及自定义加载策略。本章聚焦于如何通过 Image 加载本地图片(painterResource)和网络图片(Coil / Glide),并控制占位图(placeholder)、缩放模式(contentScale),以及在生产环境中保证加载稳定性与性能。

4.1 核心原理

Image 组件的本质是一个接收 Painter 的容器。Painter 定义了绘制方式和内容源。本地图片通过 painterResource 函数读取 drawable 资源,返回 Painter 对象;网络图片则需要引入第三方图片加载库(Coil 或 Glide),它们提供 rememberAsyncImagePainter 等 API,将 URL 转为可延迟加载的 Painter。占位图、错误图、过渡动画等均通过加载库的配置实现。

加载流程: 资源 URI / URL → 加载库解码 → Bitmap → Painter → Composable 渲染;Coil 和 Glide 默认使用内存/磁盘两级缓存,减少重复网络请求。

4.2 本地图片加载

使用 painterResource 加载 drawable 文件夹下的资源(png、webp、vector),无需额外依赖。

Image(
    painter = painterResource(id = R.drawable.ic_launcher),
    contentDescription = "应用图标",
    contentScale = ContentScale.Fit
)

对于 mipmap 或 android.resource:// 路径,可用 rememberResourcePainter(需要 @Composable 环境)。

4.3 网络图片加载(Coil)

Coil 是 Compose 官方推荐的 Kotlin 协程图片加载库,添加依赖后可直接使用 AsyncImage 或 rememberAsyncImagePainter。

依赖配置(build.gradle.kts):
implementation("io.coil-kt:coil-compose:2.6.0")

4.3.1 基本用法:AsyncImage

AsyncImage(
    model = "https://example.com/photo.jpg",
    contentDescription = "网络图片",
    placeholder = painterResource(R.drawable.placeholder),
    error = painterResource(R.drawable.error),
    contentScale = ContentScale.Crop
)

4.3.2 使用 rememberAsyncImagePainter(更灵活)

val painter = rememberAsyncImagePainter(
    model = ImageRequest.Builder(LocalContext.current)
        .data("https://example.com/photo.jpg")
        .crossfade(true)          // 渐变动画
        .size(Size(300, 300))     // 裁剪尺寸,减少内存
        .build()
)
Image(
    painter = painter,
    contentDescription = null,
    contentScale = ContentScale.FillWidth,
    modifier = Modifier.fillMaxWidth()
)

4.4 网络图片加载(Glide)

Glide 在 Compose 中通过 compose 扩展库实现,添加依赖后使用 GlideImage 或 rememberGlidePainter。

依赖注意: Glide 的 Compose 集成目前为实验性 API,建议锁定版本号 com.github.bumptech.glide:compose:1.0.0-rc02。
// 使用 GlideImage
GlideImage(
    model = "https://example.com/photo.jpg",
    contentDescription = "Glide网络图片",
    loading = painterResource(R.drawable.loading),
    failure = painterResource(R.drawable.error)
)

4.5 contentScale 缩放模式

ContentScale 决定图片如何适应 Image 的边界,常见选项如下表:

缩放模式效果适用场景
ContentScale.Fit保持宽高比,完整显示图片,可能留白图标、不需要裁剪的图
ContentScale.Crop保持宽高比,填充整个区域,裁剪边缘头像、封面图
ContentScale.FillBounds拉伸以填满边界,不保持比例背景、图案填充
ContentScale.FillWidth宽度填满,高度自适应,保持比例横屏全宽图片
ContentScale.Inside如果图片比容器小,不放大;否则等比缩小防止超大图溢出
实践建议: 列表中的图片使用 Crop 保证显示区无空白;宽屏 Banner 使用 FillWidth 配合高度约束。

4.6 实现流程总结

加载一张网络图片的标准化步骤:

  1. 添加图片加载库依赖(Coil / Glide)
  2. 在 Composable 函数中,使用 rememberAsyncImagePainter 或 AsyncImage 创建 painter
  3. 在 ImageRequest.Builder 中设置:URL、placeholder、error、crossfade、size 等
  4. 将 painter 传入 Image 组件,指定 contentScale
  5. 为 Image 添加 Modifier 控制尺寸(size、fillMaxWidth 等)
Image 组件加载流程图 Image 组件加载流程与缓存策略 Image Composable 直接使用 已有 Painter 图片加载库(Coil / Glide) 缓存命中? 内存/磁盘读取 网络请求 + placeholder Bitmap → Painter → Image 渲染 缓存策略提示: • Coil 内存缓存 LRU • Glide 磁盘缓存 250MB • 建议 size() 裁剪减少内存

4.7 调试与部署

  • 调试:在 AsyncImage 或 rememberAsyncImagePainter 的 ImageRequest.Builder 中添加 .listener(onStart = ..., onSuccess = ..., onError = ...) 观察加载状态。
  • 部署检查:确认网络库版本与 compileSdk 匹配;Coil 内部依赖 OkHttp,避免版本冲突。
  • 图片尺寸:生产环境总是通过 size(Size(x, y)) 限制解码尺寸,防止 OOM。

4.8 常见错误与解决方法

错误现象原因解决
图片不显示,无错误URL 错误 / 无网络权限检查 Manifest 中 INTERNET 权限;使用 base64 测试
OOM / 卡顿加载原图过大添加 size(Size(...)) 或使用 coil.transform 压缩
placeholder 不显示未正确设置 placeholder使用 painterResource 确保资源存在
白色闪烁未开启 crossfade 或过渡动画Coil: .crossfade(true); Glide: .transition(...)
图片变形contentScale 选用不当根据 UI 需求选择 Crop 或 Fit

4.9 性能与稳定性优化

  • 内存优化:Coil 默认使用 Bitmap.Config.HARDWARE 减少内存;Glide 使用 asBitmap() 指定格式。
  • 磁盘缓存管理:Coil 通过 Coil.imageLoader(LocalContext.current) 自定义缓存大小;Glide 可通过 RequestOptions.diskCacheStrategy() 控制。
  • 预加载:在列表滑动前使用 coil.imageLoader().enqueue(ImageRequest.Builder(context).data(url).build()) 提前下载。
  • 生命周期绑定:Coil/Glide 自动感知 Composable 生命周期,在退出时取消请求,无需手动管理。
  • 矢量图适配:本地矢量图(VectorDrawable)使用 painterResource,避免使用 Image 时被 contentScale 拉伸模糊;设置 tint 时使用 ColorFilter.tint(Color.Red)。
生产级模板: 推荐在 BaseApplication 中统一配置 Coil 的 ImageLoader,设置全局缓存策略和日志级别。
// Application 中
ImageLoader.Builder(context)
    .memoryCachePolicy(CachePolicy.ENABLED)
    .memoryCache {
        MemoryCache.Builder(context)
            .maxSizePercent(0.25)
            .build()
    }
    .diskCache {
        DiskCache.Builder()
            .directory(cacheDir.resolve("coil_cache"))
            .maxSize(100 * 1024 * 1024)
            .build()
    }
    .crossfade(true)
    .build()

4.10 小结