鸿蒙实战:字体令牌系统------AppFonts 与排版规范
前言(必读)

图:鸿蒙实战:字体令牌系统------AppFonts 与排版规范(第 68 篇) 运行效果截图(HarmonyOS NEXT)
一致的排版系统是设计质量的标志。鸿蒙应用开发中,字号 fp、圆角 radius、间距 padding、阴影 shadow 这四个维度如果有各自独立的散落定义,页面之间会出现肉眼可见的不协调。本文将详细讲解本项目的四维一体的排版令牌系统。

图:AppFonts 四维令牌体系------字号/圆角/间距/阴影的统一管理结构
#mermaid-svg-XUL2hjRJfIEFoER7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XUL2hjRJfIEFoER7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XUL2hjRJfIEFoER7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XUL2hjRJfIEFoER7 .error-icon{fill:#552222;}#mermaid-svg-XUL2hjRJfIEFoER7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XUL2hjRJfIEFoER7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XUL2hjRJfIEFoER7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XUL2hjRJfIEFoER7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XUL2hjRJfIEFoER7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XUL2hjRJfIEFoER7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XUL2hjRJfIEFoER7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XUL2hjRJfIEFoER7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XUL2hjRJfIEFoER7 .marker.cross{stroke:#333333;}#mermaid-svg-XUL2hjRJfIEFoER7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XUL2hjRJfIEFoER7 p{margin:0;}#mermaid-svg-XUL2hjRJfIEFoER7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XUL2hjRJfIEFoER7 .cluster-label text{fill:#333;}#mermaid-svg-XUL2hjRJfIEFoER7 .cluster-label span{color:#333;}#mermaid-svg-XUL2hjRJfIEFoER7 .cluster-label span p{background-color:transparent;}#mermaid-svg-XUL2hjRJfIEFoER7 .label text,#mermaid-svg-XUL2hjRJfIEFoER7 span{fill:#333;color:#333;}#mermaid-svg-XUL2hjRJfIEFoER7 .node rect,#mermaid-svg-XUL2hjRJfIEFoER7 .node circle,#mermaid-svg-XUL2hjRJfIEFoER7 .node ellipse,#mermaid-svg-XUL2hjRJfIEFoER7 .node polygon,#mermaid-svg-XUL2hjRJfIEFoER7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XUL2hjRJfIEFoER7 .rough-node .label text,#mermaid-svg-XUL2hjRJfIEFoER7 .node .label text,#mermaid-svg-XUL2hjRJfIEFoER7 .image-shape .label,#mermaid-svg-XUL2hjRJfIEFoER7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-XUL2hjRJfIEFoER7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XUL2hjRJfIEFoER7 .rough-node .label,#mermaid-svg-XUL2hjRJfIEFoER7 .node .label,#mermaid-svg-XUL2hjRJfIEFoER7 .image-shape .label,#mermaid-svg-XUL2hjRJfIEFoER7 .icon-shape .label{text-align:center;}#mermaid-svg-XUL2hjRJfIEFoER7 .node.clickable{cursor:pointer;}#mermaid-svg-XUL2hjRJfIEFoER7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XUL2hjRJfIEFoER7 .arrowheadPath{fill:#333333;}#mermaid-svg-XUL2hjRJfIEFoER7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XUL2hjRJfIEFoER7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XUL2hjRJfIEFoER7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XUL2hjRJfIEFoER7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XUL2hjRJfIEFoER7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XUL2hjRJfIEFoER7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XUL2hjRJfIEFoER7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XUL2hjRJfIEFoER7 .cluster text{fill:#333;}#mermaid-svg-XUL2hjRJfIEFoER7 .cluster span{color:#333;}#mermaid-svg-XUL2hjRJfIEFoER7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XUL2hjRJfIEFoER7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XUL2hjRJfIEFoER7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-XUL2hjRJfIEFoER7 .icon-shape,#mermaid-svg-XUL2hjRJfIEFoER7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XUL2hjRJfIEFoER7 .icon-shape p,#mermaid-svg-XUL2hjRJfIEFoER7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XUL2hjRJfIEFoER7 .icon-shape .label rect,#mermaid-svg-XUL2hjRJfIEFoER7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XUL2hjRJfIEFoER7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XUL2hjRJfIEFoER7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XUL2hjRJfIEFoER7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} AppFonts 排版系统
字号 size
圆角 radius
间距 spacing
AppShadow 阴影系统
阴影层级
xs: 10fp
sm: 12fp
md: 14fp
lg: 16fp
xl: 20fp
xxl: 28fp
sm: 8vp
md: 12vp
lg: 20vp
full: 999vp
xs: 4vp
sm: 8vp
md: 16vp
lg: 24vp
一、四维令牌全景
本项目的排版系统由四个令牌类组成,定义在同一个文件中:
typescript
// 文件:common/theme/AppFonts.ets
//
// 维度 类名 管理内容
// ─────────────────────────────────────
// 字体 AppFonts 字号阶梯 + 字重 + 字体族
// 圆角 AppRadius 6 级圆角系统
// 间距 AppSpacing 7 级间距系统
// 阴影 AppShadow 7 级阴影系统
二、AppFonts:字号阶梯
2.1 完整定义
typescript
export class AppFonts {
// 字体族
static readonly SANS: string = 'HarmonyOS Sans SC, HarmonyOS Sans'
static readonly SERIF: string = 'HarmonyOS Sans SC, Source Han Serif CN'
// 字号阶梯(fp · 跟随系统字号缩放)
static readonly DISPLAY: number = 32 // 大标题
static readonly TITLE_L: number = 28 // HarmonyOS 大标题栏
static readonly TITLE: number = 22 // 页面标题
static readonly SUBTITLE: number = 18 // 子标题
static readonly BODY_L: number = 16 // 正文大
static readonly BODY: number = 14 // 正文
static readonly CAPTION: number = 12 // 辅助文
static readonly TINY: number = 10 // 标签
// 字重
static readonly W_REGULAR: FontWeight = FontWeight.Regular
static readonly W_MEDIUM: FontWeight = FontWeight.Medium
static readonly W_BOLD: FontWeight = FontWeight.Bold
}
2.2 字号阶梯对照
32 fp ─ DISPLAY 首页品牌名「鹿鹿·笔迹心理」
28 fp ─ TITLE_L HarmonyOS 大标题栏标题
22 fp ─ TITLE 页面标题「笔迹分析报告」
18 fp ─ SUBTITLE 卡片子标题、雷达图数字
16 fp ─ BODY_L 段落正文(分享文案)
14 fp ─ BODY 常规正文
12 fp ─ CAPTION 辅助说明、标签、时间戳
10 fp ─ TINY 元数据标签、版权信息
2.3 字体族策略
typescript
// 无衬线体(正文/按钮)
static readonly SANS: string = 'HarmonyOS Sans SC, HarmonyOS Sans'
// 1. 优先 HarmonyOS Sans SC(中文字体)
// 2. 降级 HarmonyOS Sans(英文字体)
// 衬线体(品牌/标题/引用)
static readonly SERIF: string = 'HarmonyOS Sans SC, Source Han Serif CN'
// 1. 优先 HarmonyOS Sans SC(HarmonyOS 系统字体)
// 2. 降级 Source Han Serif CN(思源宋体)
选择思源宋体作为降级字体而非默认的衬线体,是因为本项目的定位是「手写 + 治愈风」,思源宋体的笔触比标准衬线体更具温度感。
三、AppRadius:圆角系统
3.1 完整定义
typescript
export class AppRadius {
static readonly XS: number = 6 // 最小圆角
static readonly S: number = 10 // 小卡片/头像
static readonly M: number = 14 // 按钮/普通卡片
static readonly L: number = 20 // 大卡片/容器
static readonly XL: number = 24 // 特圆卡片
static readonly ROUND: number = 999 // 胶囊按钮(完全圆角)
}
3.2 圆角应用场景
ROUND → [ 分享 ] 胶囊按钮 border-radius: 999px
XL → ┌──────────────┐ 特圆卡片(TapSharePage 分享卡片)
│ │
L → ┌──────────┐ 大容器卡片(标签式 AI 摘要框)
│ │
M → [ 拍照 ] 默认按钮 常规按钮
S → (头像) 小卡片 小卡片/标签
XS → ┌──────────┐ 最小圆角(强调框)
│ │
3.3 圆角设计原则
递增阶梯:6 → 10 → 14 → 20 → 24 → 999
XS S M L XL ROUND
原则:每次增加约 40-60%,肉眼可分辨。
确保相邻层级差异 ≥ 4px,避免视觉模糊。
四、AppSpacing:间距系统
4.1 完整定义
typescript
export class AppSpacing {
static readonly XXS: number = 4 // 极紧凑(图标与文字的间隙)
static readonly XS: number = 8 // 紧凑(标题与副标题的间距)
static readonly S: number = 12 // 稍紧凑(标签之间的间距)
static readonly M: number = 16 // 常规(卡片内 padding)
static readonly L: number = 20 // 稍宽松(页面左右边距)
static readonly XL: number = 28 // 宽松(模块间距)
static readonly XXL: number = 36 // 极宽松(大模块间距)
}
4.2 7 级间距使用规范
距离 层级 使用位置 示例
────────────────────────────────────────────────
4px XXS 文字与图标间隙 title + subtitle
8px XS 标题与副标题 HmTitleBar top/bottom
12px S 标签/按钮间距 情绪标签 gap
16px M 卡片内边距 padding: 16px
20px L 页面左右边距 padding(left/right: 20)
28px XL 大模块间距 首页欢迎区与 Hero 的 gap
36px XXL 最大间距 页面底部留白
4.3 调用示例
typescript
// 页面左右边距统一 20px
Column()
.padding({ left: AppSpacing.L, right: AppSpacing.L })
// 标题与副标题间距 8px
Text('标题').fontSize(AppFonts.TITLE)
Text('副标题')
.margin({ top: AppSpacing.XXS })
// 情绪标签间距 12px
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Center }) {
ForEach(this.tags, (tag: string) => {
Text(tag).margin({ right: AppSpacing.S, bottom: AppSpacing.S })
})
}
// 模块间距 28px
Scroll() {
Column({ space: AppSpacing.XL }) {
WelcomeSection()
HeroSection()
RecentReports()
InspireSection()
}
}
五、跨页面排版一致性
5.1 全应用统一 padding
所有页面的左右边距 都使用 AppSpacing.L = 20,这是 HarmonyOS Design 推荐的页面标准边距(safe area margin)。
typescript
// MetaServiceCard.ets
Column()
.padding({ left: AppSpacing.L, right: AppSpacing.L })
// CapturePage.ets
Column()
.padding({ left: AppSpacing.L, right: AppSpacing.L })
// ReportDetailPage.ets
Column()
.padding({ left: AppSpacing.L, right: AppSpacing.L })
5.2 卡片统一背景色
typescript
// 所有卡片背景统一为 CARD = #FFFCF7
Column()
.backgroundColor(AppColors.CARD)
.borderRadius(AppRadius.XL) // 特圆
// 或
.borderRadius(AppRadius.L) // 大圆角
5.3 排版一致性对比
❌ 无令牌系统时:
Page A: padding 16, fontSize 16, borderRadius 12
Page B: padding 24, fontSize 15, borderRadius 10
→ 肉眼可见的不一致
✅ 有令牌系统时:
Page A: padding AppSpacing.L, fontSize AppFonts.BODY, borderRadius AppRadius.M
Page B: padding AppSpacing.L, fontSize AppFonts.BODY, borderRadius AppRadius.M
→ 完美一致
六、与 HarmonyOS Design 的一致性
| HarmonyOS 规范 | 本项目令牌 | 值 |
|---|---|---|
| 大标题字号 | AppFonts.TITLE_L |
28fp |
| 正文字号 | AppFonts.BODY |
14fp |
| 辅助文字 | AppFonts.CAPTION |
12fp |
| 页面边距 | AppSpacing.L |
20px |
| 安全区横向 | AppSpacing.L |
20px |
| 标题字重 | AppFonts.W_MEDIUM |
Medium |
| 正文字重 | AppFonts.W_REGULAR |
Regular |
七、工程管理
规格表
| 令牌类 | 管理数量 | 文件行数 |
|---|---|---|
AppFonts |
10(字号 8 + 字重 3) | 24 行 |
AppRadius |
6 | 7 行 |
AppSpacing |
7 | 8 行 |
AppShadow |
7 | 30 行 |
全部四个类加起来不到 80 行代码,却管理了整个应用的所有排版变量。
调用频次
typescript
// 项目中部分引用统计(基于 Grep 估算)
AppFonts.BODY → 35+ 次引用 最高频
AppSpacing.L → 20+ 次引用 二级高频
AppRadius.XL → 12+ 次引用
AppShadow.CARD → 8+ 次引用
扩展建议
typescript
// 如果需要支持动态字号(系统字体缩放)
// 当前 fp 会跟随系统设置自动缩放,无需额外代码
// 如果需要支持多语言(如英文版)
// 英文环境下建议替换字体族为:
static readonly SANS_EN: string = 'SF Pro Text, Helvetica Neue'
八、注意事项与常见问题
8.1 开发注意事项
在正式开发前,建议按以下步骤完成环境准备与前置检查:
- 版本确认:检查 DevEco Studio 与 SDK 版本,确保满足目标 API Level 要求
- 权限声明 :在
module.json5的requestPermissions字段中提前声明所有需要的系统权限 - 设备能力检查:调用前验证设备是否支持目标能力(相机、NFC、传感器等)
- 异步封装 :所有耗时操作(数据库、文件 I/O、网络请求)统一使用
async/await处理 - 资源释放 :在组件
aboutToDisappear()生命周期钩子中及时释放系统资源,防止内存泄漏
8.2 常见错误与解决方案
常见问题快速排查表:
| 问题类型 | 排查方向 | 参考方法 |
|---|---|---|
| 应用崩溃 | 查看 hilog 错误日志 | hilog.error(TAG, "...", e.message) |
| 状态丢失 | 检查 AppStorage 键名拼写 | 统一使用常量管理键名 |
| 动画不流畅 | 避免在 animateTo 回调中执行 I/O | 动画与数据操作分离 |
总结
- ✅ 四维令牌系统:AppFonts / AppRadius / AppSpacing / AppShadow 统一管理
- ✅ 8 级字号阶梯:从 10fp 到 32fp,覆盖全场景
- ✅ 6 级圆角系统:XS → ROUND 递增体系
- ✅ 7 级间距系统:4px → 36px 覆盖所有间距需求
- ✅ 字体族策略:HarmonyOS Sans SC 优先 + 思源宋体降级
- ✅ 跨页面一致性:所有页面统一 padding / borderRadius / fontSize
- ✅ HarmonyOS Design 兼容:大标题 28fp、边距 20px 等符合规范
下一篇将讲解线性渐变与梯度色彩应用,看 HomePage 的 Hero CTA 渐变和情绪渐变条的实现。
📌 收藏提示:排版令牌是设计系统的骨架,项目初期就建立它能避免 80% 的排版不一致问题。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
七、排版令牌应用进阶
7.1 响应式字号策略
鸿蒙支持通过 fp 单位自动适配用户的系统字号偏好设置,令牌中所有字号均已使用 fp:
typescript
// ✅ 使用 fp,响应系统字号偏好
Text('标题').fontSize(AppFonts.sizeXl) // 20fp
// ❌ 使用 vp,不响应系统字号偏好
Text('标题').fontSize(20) // 20vp,固定不变
7.2 间距令牌的网格系统
间距令牌基于 4dp 网格,所有值均为 4 的倍数:
| 令牌 | 值 | 用途场景 |
|---|---|---|
spacingXs |
4vp | 图标与文字内间距 |
spacingSm |
8vp | 组件内部间距 |
spacingMd |
16vp | 卡片内边距 |
spacingLg |
24vp | 页面边距 |
spacingXl |
32vp | 页面区块间距 |
7.3 圆角令牌与品牌感知
圆角大小影响品牌形象:
- 直角(0vp):专业、严肃、科技感
- 小圆角(8vp):友好、现代、克制
- 大圆角(20vp):温暖、活泼、治愈感
- 全圆(999vp):按钮/标签的「药丸」形态
本项目采用中等偏大的圆角,契合「米色治愈风」的品牌定位。
7.4 令牌命名的一致性约定
四维令牌的命名遵循统一前缀规则,便于 IDE 自动补全:
| 维度 | 前缀 | 示例 |
|---|---|---|
| 字号 | size |
sizeXs / sizeSm / sizeMd / sizeLg / sizeXl |
| 圆角 | radius |
radiusSm / radiusMd / radiusLg / radiusFull |
| 间距 | spacing |
spacingXs / spacingSm / spacingMd / spacingLg |
| 行高 | lineHeight |
lineHeightSm / lineHeightMd / lineHeightLg |
输入 AppFonts. 后 IDE 会列出所有令牌,极大提高开发效率。
7.5 令牌系统的扩展路径
随着项目规模增大,四维令牌可进一步扩展:
- 字重令牌 :
fontWeightLight / fontWeightRegular / fontWeightMedium / fontWeightBold - 字族令牌 :
fontFamilyDefault / fontFamilyMono(支持自定义字体包) - 动态缩放比:基于屏幕密度(density)自动计算最终渲染尺寸
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!