图不是装饰,它有固有尺寸
图像是界面上唯一「你不完全控制」的东西——尤其当它来自用户。这一章讲三条硬约束,和一个必须加的保底。
一切都有它该有的尺寸
《Refactoring UI》里这条叫「Everything has an intended size」,说的是一个很具体的错误:把为某个尺寸设计的东西,放大或缩小到另一个尺寸用。
| 错法 | 会发生什么 | 对的做法 |
|---|---|---|
| 把 24dp 的图标放大到 64dp 当插图 | 线条粗得像儿童画——小图标为了在小尺寸下看清,笔画是加粗过的 | 用专门的大尺寸图标,或者干脆放在一个彩色圆底上 |
| 把一整张截图缩到 200dp 宽 | 里面的字全糊了,等于放了一块马赛克 | 截局部、放大关键区域,或者干脆重画一个示意图 |
| 把 Logo 缩到导航栏高度 | 细节消失,只剩一坨 | 用简化版 Logo(几乎所有品牌都有一套不同尺寸的) |
Material Icons 提供 20 / 24 / 40 / 48 四档光学尺寸——不是同一个图形的四个缩放,是四套重新画过的图形(小尺寸笔画更粗、细节更少)。
这和第 12 章那个字距是同一个道理:为一个尺寸设计的形状,换个尺寸就不对了。
用户上传的图,必须假设最坏
这是唯一一处对比度会随内容变的地方,也是无障碍最容易破功的地方。
把「图有多亮」拉到 80% 以上,选「什么都不加」——白字直接消失。
最坏 = 和文字同色、且高对比花纹(想象一张雪地照片,或者一张黑白格子布)。
所以「这张图上看着挺清楚」不是验收标准。验收标准是:换一张纯白的图,再看一次。
四种保底手段,各有代价:
| 手段 | 能保证对比度吗 | 代价 |
|---|---|---|
| 整体压暗 | 能 | 整张图都变暗了。图本身是内容时不行 |
| 底部渐变 | 能 | 几乎没有——推荐做法 |
| 文字阴影 | 不能 | 算不出确切数值,对花的图不够 |
| 实底色块 | 能,最保险 | 最不好看,切断了图 |
它只压暗文字所在的那一侧,图的主体保持原样。而且渐变在视觉上是柔和的,不像实底那样切一刀。
一个实用配方:从底部 rgba(0,0,0,.75) 到 55% 处的 rgba(0,0,0,.15) 再到顶部全透明。这个 .75 保证了即使底下是纯白的图,白字也有足够对比度。
容器决定尺寸,不是图
第三条硬约束:你不知道用户会传什么比例的图。竖的、横的、正方形的、一像素宽的。
所以布局绝不能由图的尺寸决定——必须反过来:容器定死尺寸,图往里裁。
// ✗ 让图决定高度 —— 一张竖图会把整个列表撑变形
AsyncImage(model = url, contentDescription = null)
// ✓ 容器定死,图裁进去
AsyncImage(
model = url,
contentDescription = order.productName, // 无障碍:这不是装饰图,要有描述
contentScale = ContentScale.Crop, // ← 关键:填满并裁掉多余的
modifier = Modifier
.size(72.dp) // 固有尺寸,不跟屏幕缩放(第 11 章)
.clip(RoundedCornerShape(8.dp))
.background(MaterialTheme.colorScheme.surfaceContainerHigh) // 加载前的占位
)
// 宽高比固定的封面图
AsyncImage(
model = url,
contentDescription = null, // 纯装饰图才传 null
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 9f) // ← 比例定死,不管原图什么比例
.clip(RoundedCornerShape(12.dp))
)
三个细节都值钱:
ContentScale.Crop而不是Fit——Fit会在两侧留下空白条,那些空白条的颜色你控制不了。- 背景色占位——图还没加载时那块地方不该是白的,否则列表会「闪」。
contentDescription——内容图必须有,装饰图必须显式传null。传null是在告诉读屏软件「跳过它」,而不是「我忘了写」。
图标和插画的边界
最后回到第 2 章那一问。图像也要回答「你答哪一问」:
第 20 章说过空态必须有一个能点的动作。而一张大插画会把那个按钮的视觉权重压下去(第 3 章:面积 × 亮度差)。
验收方法还是眯眼:糊一下,最重的应该是那个按钮,不是插画。如果反了,把插画调淡或者做小。
- 「图标放大一点当插图行不行」——不行。图标有光学尺寸阶梯(20/24/40/48),每档是重画的,不是缩放的。截图同理不能缩。
- 「图上的字怎么保证看得清」——加底部渐变(到
rgba(0,0,0,.75))。验收标准是换一张纯白图再看。 - 「图多大」——容器定死尺寸和比例,图用
Crop裁进去,配背景占位色。不让图决定布局。