一份活文档。它记录界面的校准、节奏与约定,每有定论便更新。
来自 androidx.compose.material.icons.outlined.* 的 Material 图标名义尺寸相同——24×24 的网格——但实际绘制的部分各不相同。若一律给它们简单的 Modifier.size(24.dp),有的会显得又大又重(Bolt、Delete),有的则小得可怜(Contacts、VpnKey)。必须手工校准。
new_size = 24 × 52 / measured_px,并夹取到 [18..28].52 像素是基准字形 Fingerprint;一切都以它为单位来衡量。
| Icon | 长度 | Why |
|---|---|---|
| Add, Apps, ArrowBack, Close | 28dp | 细线条,字形 36–41 像素 |
| AutoMode, Bolt, DeleteOutline, Edit, List, Photo, QrCode, Tune, DriveFileRenameOutline | 26dp | 中等密度,字形约 47 像素 |
| LocationOn | 24dp | 地图针脚——不要动,它是基准 |
| LocationOff | 26dp | 划掉的针脚——不要动 |
| ContentCopy, MyLocation, PeopleAlt, Wifi, ExpandMore, ExpandLess | 22dp | 偏宽或偏高,视觉上偏重 |
| Contacts, VpnKey | 20dp | 内部细节过多 |
用在原先 Bolt 出现的所有位置——密钥卡片上的“自动化”按钮、设置里的分区图标。尺寸 26 dp,与 Bolt 一致。
IconsDebugSheet.kt ——对照表 iconSizeByName。调试界面从 SettingsScreen 里的“UI 图标(调试)”按钮打开:上方是一排校准过的图标,下方是名称、尺寸和使用场景的清单。
我们使用 Material 3 的标准角色,并刻意限定为四种:
| Role | 字号 / 行高 | 用在哪里 |
|---|---|---|
titleLarge SemiBold | 22sp/28sp | 底部面板标题(“给访客 · <对象>”) |
titleMedium SemiBold | 16sp/24sp | 规则标题(“定位”“Wi-Fi”“间隔”“启用”)与 FieldSection 标签(“坐标”“半径”“模式”“已保存的网络”) |
bodyMedium | 14sp/20sp | 单选按钮说明、胶囊按钮内的文字、分段标签 |
bodySmall muted | 12sp/16sp | 状态与提示(“距该点 36540 米”“74 米 · 上限,访客可下调”,以及提示文字)。颜色: onSurfaceVariant |
我们约定按以下数值的倍数工作: 2 / 4 / 8 / 10 / 14 / 16 / 20。不要忽疏忽密:相同的元素给相同的间距。
spacedBy(12.dp)。卡片彼此相距 12 dp。padding(horizontal=20.dp).padding(top=16.dp).verticalArrangement = spacedBy(16.dp) ,用于各大块之间。spacedBy(2.dp)。加粗标签与其下的状态或提示之间只留 2 dp,好让二者读作“标题+说明”的一对。Spacer(10.dp)。在“定位”、Wi-Fi 连接和 Wi-Fi 列表中同理。由于最小触控区域,Material 3 的滑块在布局中占去约 48 dp,其中可见轨道只有中间约 4 dp,上下各约 22 dp 是空白触控区。因此:
Modifier.offset(y=-16.dp) ,把滑块拉近文字。Modifier.sliderTrim() (位于 GuestAutomationEditor.kt的辅助函数),它向父级报告 22 dp 而不是 48 dp 的高度。这样卡片的 padding(16.dp) 让滑块到底边的间隙,与以胶囊按钮收尾的卡片保持一致。每条规则——定位、Wi-Fi、时间、授权、确认——以及“启用”块,都取三个层级之一。开关打开时,卡片的图标圆圈会染上对应颜色。
| Key | Label | Colour | Description |
|---|---|---|---|
own |
由访客决定 | #3BA55D (green) | 访客自行选择设置 |
suggest |
Preset | #E5B409 (yellow) | 数值已设定,访客可以更改 |
force |
Locked | #CC3322 (red) | 数值已设定,访客无法更改 |
开关关闭时圆圈变灰(#9AA0A6),无论选了哪个层级。
全应用统一的 38 dp 落位槽:
CircleShape,背景取层级颜色。contentAlignment = Alignment.Center.把它们包进 Box(Modifier.size(38.dp), contentAlignment=Center) { RadioButton(...) } ,好让单选圆点的中心与上方彩色圆圈的中心对齐。没有这层包装,单选按钮会水平偏移,因为 RadioButton 自带内边距。
单选按钮与其文字之间: Spacer(14.dp)。同一组单选行之间: Column(spacedBy(4.dp)).
最小触控区域已关闭: CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) ——否则每个单选行都会膨胀到 48 dp,破坏节奏。
Material 3 的标准开关,位于卡片标题右侧。它把整条规则关掉,此时不显示层级颜色——圆圈变灰。
用于在规则内切换模式的自制控件——目前只在 Wi-Fi 里:“按指定网络” / “按周围网络”。Row 中放两项,各自带 weight(1f):
colorScheme.surface, RoundedCornerShape(10.dp),内边距 4dp。primaryContainer, RoundedCornerShape(8.dp),文字用 SemiBold。带描边、图标和文字,完全圆角。用于次要操作——“使用当前位置”“采集周围网络”。禁用时描边与文字降到 alpha=0.4.
关于 offset(y=-16.dp) 与 sliderTrim(),见第 3 节。滑块之前总有一个 StatusLine ,承载状态文字(bodySmall,弱化色)——“74 米 · 上限,访客可下调”。
点击卡片标题行即可打开——除开关外的整行。它出现在标题正下方,横跨卡片内容的整个宽度。为此:
Box ,带 onGloballyPositioned,并以 dp 测量其宽度。DropdownMenuItem 接收该宽度,配合修饰符 Modifier.width(anchorWidthDp).下拉项是会呼吸的,而非扁平的行:左侧一个色标,标签加粗,其下是小号弱化字体的说明。垂直方向按第一行也就是标签对齐。
与规则那些普通彩色圆圈不同,“启用”标题处的圆圈会脉动。
drawBehind + Stroke(width=6dp).LinearEasing. 不要用 FastOutSlowInEasing ——否则周期重启时会看到一次明显的跳动。sin(π·p) × 0.6:在 p=0 与 p=1 时透明度为 0,中间达到峰值。重启因此看不出来。界面文案中的单句不加句号。 只有两句及以上时才用句号来分隔。这适用于所有说明、提示、状态、悬浮提示、标题和独立短语。
在黄色(已设定)模式的滑块状态里,我们直接说明自由的方向:
红色(已锁定):“74 米 · 恰好” / “7 中 5 · 恰好”。
“由访客决定 / 已设定 / 已锁定”已经固定下来,不再更改。
若规则存在关闭态,就用开关表达,而不要用“不需要 / 否 / 关闭”之类的单选项。否则用户有两种方式关掉同一件事,令人困惑。
Examples:
在“坐标”块中,复制与删除图标位于右侧 ,与两行齐平 (标签与状态)。实现方式为 Row { Column(weight=1f) { label; status }; CopyIcon; DeleteIcon }。图标相对整对文本做垂直居中。
主体仅在以下情况展开:
Animation: expandVertically(tween(220)) + fadeIn(tween(220)) 用于展开, shrinkVertically(tween(180)) + fadeOut(tween(120)) 用于收起。
收到访客密钥时——仅当应用在前台且密钥是新的而非重复的。否则静默保存。
sin(π·p) × 0.6。任何简化都会毁掉观感。最后更新:2026 年 4 月 18 日 · 文档版本 2.07