一行到底放多少字
这件事不归审美管,归眼睛的回扫动作管。而且它是全书少数几个「宽屏比窄屏更容易做错」的地方——手机上你想错也错不了,平板上你不管它就一定错。
规矩和它的来历
排版界的老规矩:拉丁文正文,一行 45 到 75 个字符(含空格),最佳值在 66 附近。
来历不是美学,是回扫(return sweep)——眼睛读完一行,要跳回下一行的行首。这个动作有两个失败模式:
- 行太长:跳回去的时候容易落到错的行上,也就是「串行」。而且一行读到一半时,你已经忘了这行开头说了什么。
- 行太短:回扫太频繁,阅读被切成一段一段,节奏很碎。
量出来
「一行几个字符」不是数出来的,是量出来的:用 Roboto 里 26 个小写字母的平均前进宽度(1022.38 / 2048 em),乘字号,再去除容器宽度。
拉动滑杆,注意这几个结果:
| 场景 | 宽度 | 16px 正文每行 | 判定 |
|---|---|---|---|
| 手机竖屏 | 360dp(内容区 328) | 41.1 字符 | 略窄 |
| 大屏手机 | 411dp(内容区 379) | 47.5 字符 | 舒服 |
| 小平板 | 600dp(内容区 552) | 69.1 字符 | 舒服 |
| 平板 / 小窗 | 840dp(内容区 792) | 99.2 字符 | 太宽 |
| 桌面 | 1280dp(内容区 1232) | 154.3 字符 | 远远太宽 |
手机竖屏(360dp)几乎卡在下限上。这意味着在手机上,你想把正文做得「太宽」是不可能的——所以这一章对纯手机 App 几乎不构成约束。
但只要屏幕超过 600dp,正文满宽就一定错。而这正是大多数 Android App 在平板上难看的头号原因:它们只是把手机布局拉宽了。
中文的数
中文没有空格分词,每个汉字宽度都是一个 em,所以换算简单得多:
每行汉字数 = 容器宽度 / 字号
经验区间是每行 20 到 38 个汉字,最佳 28 到 32。
你现在读的这段正文是 17px,画板内容区约 600px,也就是每行大约 35 个汉字——按 M 键,标注模式会把它量出来。
拉丁文的单词有长有短、有升部有降部,这些不规则的轮廓给眼睛提供了「路标」,回扫时容易定位。
汉字全是等宽的方块,一行看过去没有任何路标。所以中文既需要更松的行距(上一章那个 1.6–1.8),也需要更短的行长。
怎么限宽
知道了要限宽,剩下的是在哪儿限。有三种做法,优劣分明:
// ✗ 最差:按百分比。屏幕越宽,正文越长,问题原封不动
Text(text, Modifier.fillMaxWidth(0.8f))
// △ 凑合:写死一个最大宽度。能用,但那个数是拍的
Text(text, Modifier.widthIn(max = 600.dp))
// ✓ 最好:从「我想要几个字符」反推宽度,这个数有出处
val maxTextWidth = with(LocalDensity.current) {
// 66 字符 × 平均字宽(从字体度量来)
(66 * 0.499f * MaterialTheme.typography.bodyLarge.fontSize.toPx()).toDp()
}
Text(text, Modifier.widthIn(max = maxTextWidth))
// 更省事的写法:宽了就分栏,而不是拉长
when (windowSizeClass.widthSizeClass) {
WindowWidthSizeClass.Compact -> SingleColumn()
WindowWidthSizeClass.Medium -> SingleColumn(maxWidth = 600.dp)
WindowWidthSizeClass.Expanded -> TwoPane() // 列表 + 详情
}
那个 0.499 是 Roboto 小写字母的平均前进宽度(1022.38 / 2048),来自上一章那台探针。这就是「有出处」的样子——三个月后有人问「为什么是 600dp」,答案不是「看着差不多」,是「66 个字符」。
「响应式」真正在解决什么
这一章值得引出一个更大的结论,因为它纠正了一个很普遍的误解。
是「屏幕变大时,改变布局,而不是拉伸内容」。
《Refactoring UI》里有一节叫「Grids are overrated」,说的就是这件事:不要把什么都做成百分比。界面上的量分成两类——
- 有固有尺寸的:字号、行高、图标、头像、按钮高度、触摸目标。这些由人的眼睛和手指决定,和屏幕多大毫无关系。48dp 的按钮在平板上还是 48dp。
- 该响应的:容器宽度、栏数、间距档位、以及「显示几栏」这类布局决策。
把第一类做成百分比,就会得到「平板上一切都被放大了 2 倍」的效果——那看起来像一个给老年人用的手机 App。
Material 3 的窗口尺寸类给的就是第二类的决策点:
| 尺寸类 | 宽度 | 典型设备 | 该做的布局决策 |
|---|---|---|---|
| Compact | < 600dp | 手机竖屏 | 单栏,底部导航 |
| Medium | 600–839 | 手机横屏 / 小平板 | 单栏但限宽,导航改侧边栏 |
| Expanded | 840–1199 | 平板 / 小窗桌面 | 双栏:列表 + 详情 |
| Large | 1200–1599 | 桌面 | 双栏 + 常驻侧边栏 |
| Extra large | ≥ 1600 | 超宽桌面 | 限住总宽,别再摊开了 |
屏幕再宽,内容也该有个上限。一个 2560dp 宽的显示器上,把内容摊满等于强迫用户左右转头。
这也是为什么几乎所有内容型网站在大屏上都是「中间一条,两边留白」——那些留白不是浪费,是在保住行长。
- 「正文容器多宽」——从「66 个字符」反推,不是拍一个 600dp。拉丁文 45–75 字符,中文 20–38 字。
- 「屏幕变宽了怎么办」——改布局(限宽、分栏),不拉伸内容。有固有尺寸的东西(字号、图标、触摸目标)永远不跟着屏幕缩放。