Compose中Image的使用

3 阅读5分钟

一、核心概念与基础用法

1. 核心参数

Image 最基础的构造函数包含 3 个核心参数:

参数名作用
painter图片绘制器(核心,指定图片来源)
contentDescription图片描述(无障碍适配,必填;无描述则传 null,但需加 Modifier.semantics { contentDescription = null }
modifier布局 / 样式修饰符(尺寸、裁剪、缩放、点击等)

2. 基础示例(加载本地资源图片)

@Composable
fun BasicImageDemo() {
    // 加载 res/drawable 目录下的图片(如 ic_launcher.png)
    Image(
        painter = painterResource(id = R.drawable.ic_launcher_background),
        contentDescription = "应用图标", // 无障碍描述
        modifier = Modifier
            .size(100.dp) // 设置图片宽高(宽高相同用 size,不同用 width/height)
            .padding(16.dp)
    )
}

二、图片来源(painter 的多种实现)

painter 是 Image 的核心,不同图片来源对应不同的 Painter 实现:

图片来源实现方式适用场景
本地资源(res/drawable)painterResource(id = R.drawable.xxx)应用内置图片(图标、背景)
网络图片第三方库(Coil/Glide)提供的 rememberAsyncImagePainter远程图片(接口返回的 URL)
本地文件 / UrirememberImagePainter(File("/path/to/img"))(Coil)手机本地文件图片
矢量图(VectorDrawable)painterResource(id = R.drawable.ic_vector)矢量图标(适配不同分辨率)

关键示例:加载网络图片(主流方案:Coil 库)

Compose 官方推荐用 Coil 加载网络图片,步骤如下:

  1. 添加依赖(build.gradle.kts):
dependencies {
    // Coil for Compose
    implementation("io.coil-kt:coil-compose:2.6.0")
}

2.使用示例

@Composable
fun NetworkImageDemo() {
    val imageUrl = "https://img1.baidu.com/it/u=600722015,3838115472&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=750"

    // 加载网络图片(rememberAsyncImagePainter 自动处理缓存、加载状态)
    val painter = rememberAsyncImagePainter(
        model = imageUrl,
        // 配置加载占位符/错误占位符
        placeholder = painterResource(id = R.drawable.ic_launcher_background),
        error = painterResource(id = R.drawable.ic_launcher_background)
    )

    Image(
        painter = painter,
        contentDescription = "网络图片",
        modifier = Modifier
            .size(200.dp)
            .clip(RoundedCornerShape(8.dp)), // 圆角裁剪
        contentScale = ContentScale.Crop // 缩放模式
    )
}

三、核心样式配置

1. 缩放模式(contentScale)

控制图片如何适配 Image 的尺寸(类似 ImageView 的 scaleType),常用值:

作用
ContentScale.Fit等比例缩放,图片完整显示(可能留空白)
ContentScale.Crop等比例缩放,填满容器(超出部分裁剪,常用作头像 / 封面)
ContentScale.FillBounds拉伸图片填满容器(可能变形,不推荐)
ContentScale.Center图片居中显示,不缩放(超出部分裁剪)
ContentScale.Inside类似 Fit,但图片尺寸不超过原始尺寸

示例:

Image(
painter = painterResource(id = R.drawable.test_img),
contentDescription = "缩放示例",
modifier = Modifier.size(200.dp),
contentScale = ContentScale.Crop // 裁剪填充
)

2. 裁剪与形状(modifier + Clip)

通过 modifier.clip() 实现图片裁剪,支持多种形状:

@Composable
fun ImageClipDemo() {
    Column(modifier = Modifier.padding(16.dp)) {
        // 1. 圆角图片
        Image(
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "圆角图片",
            modifier = Modifier
                .size(100.dp)
                .clip(RoundedCornerShape(16.dp)), // 圆角半径
            contentScale = ContentScale.Crop
        )

        Spacer(modifier = Modifier.height(16.dp))

        // 2. 圆形图片(头像常用)
        Image(
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "圆形头像",
            modifier = Modifier
                .size(100.dp)
                .clip(CircleShape), // 圆形裁剪
            contentScale = ContentScale.Crop
        )

        Spacer(modifier = Modifier.height(16.dp))

        // 3. 自定义形状(如矩形带倒角)
        Image(
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "自定义形状",
            modifier = Modifier
                .size(100.dp)
                .clip(RoundedCornerShape(topStart = 20.dp, bottomEnd = 20.dp)),
            contentScale = ContentScale.Crop
        )
    }
}

3. 边框与阴影

结合 border 和 shadow 修饰符给图片加边框 / 阴影:

@Composable
fun ImageBorderShadowDemo() {
    Image(
        painter = painterResource(id = R.drawable.ic_launcher_background),
        contentDescription = "带边框和阴影的图片",
        modifier = Modifier
            .size(100.dp)
            .shadow( // 阴影:半径、颜色、偏移
                elevation = 8.dp,
                shape = CircleShape,
                //color = Color.Gray.copy(alpha = 0.5f)
            )
            .clip(CircleShape) // 先裁剪形状,再加边框
            .border( // 边框:宽度、颜色、形状
                width = 2.dp,
                color = Color.Blue,
                shape = CircleShape
            ),
        contentScale = ContentScale.Crop
    )
}

4. 点击事件

通过 modifier.clickable 给图片加点击 / 长按事件:

@Composable
fun ClickableImageDemo() {
    //在最外层获取
    val context = LocalContext.current
    Image(
        painter = painterResource(id = R.drawable.ic_launcher_background),
        contentDescription = "可点击图片",
        modifier = Modifier
            .size(100.dp)
            .clip(CircleShape)
            .clickable( // 点击事件
                onClick = {
                    Toast.makeText(context, "图片被点击", Toast.LENGTH_SHORT).show()
                },
            ),
        contentScale = ContentScale.Crop
    )
}

四、进阶用法

1. 加载状态处理(网络图片)

监听网络图片的加载状态(加载中、成功、失败),自定义界面:

@Composable
fun ImageLoadingStateDemo() {
    val imageUrl = "https://img2.baidu.com/it/u=2376489989,3127732063&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=657"
    val painter = rememberAsyncImagePainter(
        model = imageUrl,
        placeholder = painterResource(id = R.drawable.ic_launcher_background)
    )

    // 获取加载状态
    val state = painter.state
    Column(modifier = Modifier.padding(16.dp)) {
        Image(
            painter = painter,
            contentDescription = "加载状态示例",
            modifier = Modifier.size(200.dp),
            contentScale = ContentScale.Crop
        )

        // 根据状态显示提示文字
        when (state) {
            is AsyncImagePainter.State.Loading -> {
                Text(text = "图片加载中...", modifier = Modifier.padding(top = 8.dp))
            }
            is AsyncImagePainter.State.Success -> {
                Text(text = "图片加载成功", modifier = Modifier.padding(top = 8.dp))
            }
            is AsyncImagePainter.State.Error -> {
                Text(
                    text = "图片加载失败",
                    color = Color.Red,
                    modifier = Modifier.padding(top = 8.dp)
                )
            }
            else -> {}
        }
    }
}

2. 图片淡入动画

给图片添加加载完成后的淡入效果:

@Composable
fun ImageFadeInDemo() {
    val imageUrl = "https://img2.baidu.com/it/u=2376489989,3127732063&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=657"
    val painter = rememberAsyncImagePainter(model = imageUrl)
    // 动画状态:加载成功后从 0f 变为 1f
    val alpha by animateFloatAsState(
        targetValue = if (painter.state is AsyncImagePainter.State.Success) 1f else 0f,
        animationSpec = tween(durationMillis = 500) // 动画时长 500ms
    )

    Image(
        painter = painter,
        contentDescription = "淡入图片",
        modifier = Modifier
            .size(200.dp)
            .alpha(alpha), // 透明度动画
        contentScale = ContentScale.Crop
    )
}

五、性能优化与注意事项

1. 性能优化

  • 缓存图片资源:本地图片用 remember 缓存 Painter(避免每次重组重新加载):

    kotlin

    val painter = remember { painterResource(id = R.drawable.test_img) }
    
  • 网络图片优化:Coil 已内置缓存(内存 + 磁盘),无需额外处理;避免频繁修改 model(如 URL)导致重复请求。

  • 尺寸优化:给 Image 明确设置 size/width/height,避免图片自适应导致的额外布局计算。

  • 避免过度绘制:图片加背景时,优先用 modifier.background() 而非嵌套 Box,减少层级。

2. 常见坑点

坑点解决方案
contentDescription 报错必传参数:有描述则传字符串,无描述则传 null + Modifier.semantics { contentDescription = null }
图片拉伸变形用 contentScale = ContentScale.Crop/Fit,避免 FillBounds
圆形图片有锯齿给 clip(CircleShape) 前加 shadow(微小阴影),或用高分辨率图片
网络图片加载失败无提示监听 painter.state,显示错误占位符 / 提示文字
图片边框超出裁剪范围先 clip 再 border(顺序不能反),且边框形状和裁剪形状保持一致

六、总结

  1. Image 核心是 painter(图片来源) + modifier(布局 / 样式) + contentScale(缩放),本地图片用 painterResource,网络图片优先用 Coil 的 rememberAsyncImagePainter
  2. 样式配置重点:contentScale 控制缩放(Crop/Fit 最常用),clip 控制裁剪(圆形 / 圆角),border/shadow 增强视觉效果;
  3. 进阶技巧:监听加载状态实现占位 / 错误提示,结合动画实现淡入效果,注意性能优化(缓存、明确尺寸)和坑点(参数顺序、无障碍描述)。