6、图片与图标:Image组件、加载网络图片(Coil)、Icon与IconButton、图标库使用。
好,咱们今天来聊聊图片和图标。这两个东西在App里太常见了,几乎每个界面都离不开。你想想看,一个没有图片、没有图标的App,那得多枯燥?
在Jetpack Compose里,处理图片和图标的方式跟传统View体系不太一样。我刚开始从XML切到Compose时,第一反应是:「嗯?Image怎么用?Glide还能用吗?」后来发现,Compose有自己的一套玩法,而且用起来更顺手。
6.1 Image组件:显示图片的基础
Image组件是Compose里最基础的图片显示方式。它支持从资源、文件、Bitmap等多个来源加载图片。
@Composable
fun BasicImageExample() {
// 从资源加载
Image(
painter = painterResource(id = R.drawable.sample_image),
contentDescription = "示例图片",
modifier = Modifier
.size(200.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
}
这里有几个关键参数:
- painter:图片的绘制器,决定了图片从哪里来
- contentDescription:无障碍描述,别忽略它
- contentScale:图片的缩放模式,类似ImageView的ScaleType
- modifier:修饰符,可以控制大小、形状、点击事件等
contentScale有几种常用模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| ContentScale.Fit | 保持宽高比,完整显示 | 展示完整图片 |
| ContentScale.Crop | 保持宽高比,裁剪填充 | 头像、封面图 |
| ContentScale.FillBounds | 拉伸填充,不保持比例 | 背景图(慎用) |
| ContentScale.Inside | 保持比例,缩小到容器内 | 防止图片溢出 |
6.2 加载网络图片:Coil的妙用
实际项目中,图片大多来自网络。在Compose里,最流行的方案是Coil。为什么选它?因为它是专门为Kotlin协程和Compose设计的,轻量、高效。
先加依赖:
// build.gradle.kts (Module)
implementation("io.coil-kt:coil-compose:2.6.0")
然后就可以用了:
@Composable
fun NetworkImageExample() {
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = "网络图片",
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
contentScale = ContentScale.Crop,
placeholder = painterResource(R.drawable.placeholder),
error = painterResource(R.drawable.error_image)
)
}
AsyncImage是Coil提供的Composable组件。它自动处理了:
- 图片下载和缓存
- 内存和磁盘缓存策略
- 加载中和加载失败的占位图
- 图片的取消和复用
核心要点:Coil默认使用内存缓存和磁盘缓存。你不需要手动管理缓存,它会自动帮你处理好。我在项目中遇到过一个问题:图片更新了但App里还是旧的。后来发现是Coil的缓存策略导致的,用diskCachePolicy和memoryCachePolicy可以控制。
如果你需要更精细的控制,可以用ImageRequest:
@Composable
fun AdvancedCoilExample() {
val imageLoader = ImageLoader(context) {
memoryCachePolicy(CachePolicy.ENABLED)
diskCachePolicy(CachePolicy.ENABLED)
crossfade(true)
}
AsyncImage(
model = ImageRequest.Builder(context)
.data("https://example.com/image.jpg")
.crossfade(true)
.size(500, 500)
.build(),
imageLoader = imageLoader,
contentDescription = "高级加载示例"
)
}
6.3 Icon与IconButton:小而美的图标
图标在UI里扮演着「视觉语言」的角色。一个点赞图标、一个分享图标,用户一看就懂,不需要文字解释。
Compose里用Icon组件显示图标:
@Composable
fun IconExample() {
Icon(
imageVector = Icons.Default.Home,
contentDescription = "首页",
tint = Color.Blue,
modifier = Modifier.size(24.dp)
)
}
IconButton则是可点击的图标按钮:
@Composable
fun IconButtonExample() {
IconButton(onClick = { /* 处理点击 */ }) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = "收藏",
tint = Color.Red
)
}
}
这里有几个要点:
- imageVector:矢量图标,缩放不失真
- tint:图标着色,可以统一颜色
- contentDescription:无障碍描述,必须提供
- IconButton自带点击涟漪效果,不需要额外处理
val AppIcons.Home = Icons.Default.Home,这样在多个地方引用时保持一致,也方便后续替换图标库。
6.4 图标库使用:Material Icons与自定义
Compose默认集成了Material Icons库。你只需要在依赖里加上:
// build.gradle.kts
implementation("androidx.compose.material:material-icons-extended:1.6.0")
然后就可以使用所有Material图标了:
Icon(
imageVector = Icons.Filled.AccountCircle,
contentDescription = "用户头像"
)
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = "设置"
)
Icon(
imageVector = Icons.Rounded.Star,
contentDescription = "评分"
)
Material Icons分为几个风格:
| 风格 | 前缀 | 特点 |
|---|---|---|
| Filled | Icons.Filled | 实心填充,最常用 |
| Outlined | Icons.Outlined | 轮廓风格,更轻盈 |
| Rounded | Icons.Rounded | 圆角风格,更柔和 |
| Sharp | Icons.Sharp | 尖锐风格,更硬朗 |
| TwoTone | Icons.TwoTone | 双色调,更丰富 |
如果你需要自定义图标,可以用ImageVector.Builder:
val CustomIcon = ImageVector.Builder(
name = "CustomStar",
defaultWidth = 24.dp,
defaultHeight = 24.dp,
viewportWidth = 24f,
viewportHeight = 24f
).apply {
addPath(
pathData = PathParser().parsePathString("M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z").toNodes(),
fill = SolidColor(Color.Black)
)
}.build()
不过说实话,我很少自己画路径。一般用SVG转成VectorDrawable,然后通过painterResource加载。这样更省事,设计师给什么就用什么。
避坑指南:material-icons-extended库很大(约30MB+),如果你只用了少量图标,建议用Icons.Default(只包含基础图标),或者用R8/ProGuard做资源裁剪。我曾经因为没注意这个,导致APK体积暴增,后来用Icons.Default替换了大部分图标,体积降了20%。
知识体系总览
下面这张图帮你理清本章的核心脉络:
嗯,图片和图标这部分内容其实不难,但细节挺多。我个人觉得,最重要的是养成好习惯:
- 图片一定要有占位图和错误图
- 图标一定要有contentDescription
- 网络图片用Coil,别自己写下载逻辑
- 图标库按需引入,别一股脑全加进来
这些习惯能帮你避免很多线上问题。我在项目里见过太多因为图片加载失败导致界面空白的案例,也见过因为图标库太大导致APK体积爆炸的情况。说白了,这些坑都是可以提前避开的。