“好看还好用?不就是俩词嘛!”——鸿蒙 UI/UX 设计原则与工具全攻略(含可复用代码片段)
因为场景被忽视:车上、中控屏、手表、小折叠、平板、甚至与手机协同的“超级终端”,输入法、可视距离、交互方式统统不同。系统级分布式能力 + 一致的设计语言(ArkUI + 设计令牌)。换句话说,别把每个屏当“孤岛”;你在一个屏上做的决定,最好能“自然地”在其他屏延续。在鸿蒙生态里做 UI/UX,比“画个好看的界面”难一点点,但也有章可循令牌化 → 栅格与断点 → 状态与动效 → 可访问性 → 分布式
我是兰瓶Coding,一枚刚踏入鸿蒙领域的转型小白,原是移动开发中级,如下是我学习笔记《零基础学鸿蒙》,若对你所有帮助,还请不吝啬的给个大大的赞~
开篇
实话实说,很多界面“看着能打”,一上手就泄气:点不着、慢半拍、弹窗跳你脸上,夜间模式还刺眼。别怪用户“矫情”,在鸿蒙(HarmonyOS / OpenHarmony)生态里,多设备、多形态、分布式协作就是日常,UI/UX 一旦偷懒,体验立刻原形毕露。今天我们不摆花架子,从原则到工具、从设计到落地,把鸿蒙端上如何做好看且好用的界面说清楚;顺手给上ArkUI/ArkTS的实战代码,拿去改名就能跑。🙂
前言:为什么“好看的界面”常常不好用?
因为场景被忽视:车上、中控屏、手表、小折叠、平板、甚至与手机协同的“超级终端”,输入法、可视距离、交互方式统统不同。鸿蒙的价值在于:系统级分布式能力 + 一致的设计语言(ArkUI + 设计令牌)。换句话说,别把每个屏当“孤岛”;你在一个屏上做的决定,最好能“自然地”在其他屏延续。
一张图看懂:鸿蒙 UI/UX 的“骨架”
[设计目标] → 一致、易学、可预期、无打扰
│
[设计令牌/主题] → 颜色/字体/间距/动效曲线
│
[布局与导航] → 栅格/自适配/分栏/分屏/手势导航
│
[状态与反馈] → 可点击即反馈/加载占位/空状态/错误恢复
│
[动效与节奏] → 传达层级/引导注意/弱提醒
│
[可访问性] → 对比度/大字体/语音辅助/触控热区
│
[跨设备一致] → 超级终端协同/分布式状态同步
核心设计原则(用人话说透)
-
一致性(Consistency)
组件行为在不同页面、不同设备保持相同心智模型:返回逻辑、手势区域、状态颜色不“换口味”。 -
可预期(Predictability)
交互后立即给反馈:轻震、涟漪、骨架屏别省;二级加载用渐进式渲染,不要“黑屏等奇迹”。 -
信息有层次(Hierarchy)
当屏信息>7个元素,必须建层级:视觉权重(字号/对比)、空间分组(卡片/分组标题)、动效先后引导。 -
克制的动效(Subtle Motion)
动效是“语法”,不是烟花:入场≤300ms、状态切换≤200ms;曲线用加速-减速而非匀速。 -
自适配优先(Responsive First)
先画栅格与断点,再摆组件;不要硬写 px,把密度和留白当“一等公民”。 -
无打扰(Calm Design)
通知/弹窗分级:极少数“打断式”,大多数“低打扰”(Banner/Toast/角标),驾驶/运动等场景更要克制。 -
可访问性(A11y)
触控热区≥44dp,文字可达动态字体级别,色彩对比建议主文本≥4.5:1。
设计令牌(Design Tokens):把“审美”变成可复用资产
在鸿蒙端,主题/样式最好都落到“令牌”上:颜色、字号、间距、圆角、动效曲线 → 一处定义,多处复用。
// /common/theme/Tokens.ets
export const Tokens = {
color: {
brand: '#3460ff',
brandBg: '#EDF2FF',
textPrimary: '#111827',
textSecondary: '#6B7280',
danger: '#EF4444',
success: '#10B981',
surface: '#FFFFFF',
surfaceAlt: '#F8FAFC'
},
radius: { xs: 6, sm: 10, md: 14, lg: 20 },
space: { xs: 6, sm: 10, md: 16, lg: 24, xl: 32 },
font: { h1: 28, h2: 22, h3: 18, body: 16, small: 14 },
motion: { fast: 120, base: 200, slow: 280 } // 毫秒
}
暗色模式:只需切换令牌表即可,无需满项目搜颜色替换。
布局与导航:栅格、分栏、分屏,一个都不能少
栅格 + 断点(ArkUI 示例)
// /pages/Home.ets
import { Tokens } from '../common/theme/Tokens'
@Entry
@Component
struct Home {
@State cols: number = 2
onPageShow() {
const w = px2vp(Screen.width); // 逻辑宽度
this.cols = w >= 900 ? 4 : (w >= 600 ? 3 : 2);
}
build() {
Column({ space: Tokens.space.md }) {
Text('发现').fontSize(Tokens.font.h1).fontWeight(FontWeight.Bold)
Grid({ columns: this.cols }) {
ForEach(this.mockCards(), (card) => {
GridItem() {
Card().backgroundColor(Tokens.color.surface).borderRadius(Tokens.radius.lg) {
Column({ space: Tokens.space.sm }) {
Text(card.title).fontSize(Tokens.font.h3)
Text(card.desc).fontColor(Tokens.color.textSecondary).fontSize(Tokens.font.small)
}.padding(Tokens.space.md)
}.onClick(() => this.onCardTap(card))
}
})
}.margin({ top: Tokens.space.md })
}.padding(Tokens.space.lg).backgroundColor(Tokens.color.surfaceAlt)
}
private mockCards() { /* …返回卡片数据… */ }
private onCardTap(card) { /* …导航… */ }
}
分屏与分栏(Master-Detail 模式)
- ≥900dp:左列表 + 右详情
- 600–900dp:单列 + 抽屉
- <600dp:全屏栈式导航
规则先定清楚,交互与动效自然顺畅。
状态与反馈:骨架屏、空状态、错误页“铁三角”
// Skeleton 占位
@Component
struct ListSkeleton {
build() {
Column({ space: 12 }) {
ForEach(Array.from({length: 6}), (_, i) => {
Row() {
Shimmer({ width: 56, height: 56, radius: 8 }) // 头像骨架
Column({ space: 8 }) {
Shimmer({ width: '60%', height: 16, radius: 4 })
Shimmer({ width: '40%', height: 14, radius: 4 })
}.margin({ left: 12 })
}
})
}
}
}
空状态写作:一句话解释 + 一步可行动作(按钮/刷新/授权)。
错误恢复:本地兜底 → 重试按钮 → 反馈通道,别把“重试”藏三级菜单。
动效:控制节奏,而不是“炫酷一时”
// 关键区块的入场过渡
@State show: boolean = false
build() {
Column() {
Button('加载数据').onClick(() => {
this.show = false
animateTo({ duration: Tokens.motion.base }, () => this.show = true)
})
if (this.show) {
Column() {
// 内容…
}
.transition(TransitionEffect.OPACITY) // 简洁的淡入
}
}
}
动效准则
- 入场 ≤300ms;状态切换 ≤200ms。
- 避免“位移+缩放+阴影+模糊”一锅炖;一次改变不超过两种维度。
- 关键动效可配轻震反馈(如按钮成功)。
可访问性(A11y):把“可用”做给所有人
- 触控热区 ≥44dp;表单项间距 ≥12dp;多选/单选点击区域包含标签文本。
- 文本对比主内容 ≥4.5:1;暗色模式注意蓝色系“发光感”。
- 给交互控件添加无障碍描述与焦点顺序。
Button('提交')
.accessibilityText('提交表单')
.accessibilityGroup(true)
表单与反馈:高转化的“三件套”
- 内联校验:离焦即校,不要“一键提交才报十条错”。
- 分段提交:长表单拆段,有进度与草稿保存。
- 成功页:别只说“成功”,给下一步行动(返回/查看/分享)。
// 表单片段(内联校验示意)
@State email: string = ''
@State emailErr: string = ''
TextInput({ placeholder: '邮箱' })
.onChange((v) => {
this.email = v
this.emailErr = /\S+@\S+\.\S+/.test(v) ? '' : '邮箱格式不正确'
})
if (this.emailErr) {
Text(this.emailErr).fontColor(Tokens.color.danger).fontSize(12)
}
主题与暗色模式:一键反转不是“把黑改成白”
- 对比关系保持不变:强调色只变亮度,不要乱改色相。
- 阴影→亮边(暗色里阴影弱化),投影与层级要重设。
- 插画/图片:准备浅/深双套或用 CSS 滤镜/着色。
// 主题切换(示意)
@State dark: boolean = false
const theme = this.dark ? DarkTokens : Tokens;
资源与国际化:从 Day 1 就考虑
- 多语言资源目录:
/resources/zh_CN,/en_US… - 长度膨胀:德语/俄语会很长;按钮文字尽量用可扩展空间。
- 日期/货币/单位:别写死,交给格式化函数。
性能与流畅性:60FPS 靠“少做事 + 做该做的事”
LazyForEach按需渲染,长列表分帧加载。- 图片尽量用合适分辨率与缓存;矢量图标优先。
- 避免在 UI 线程做重计算,后台/异步处理后差分更新。
协同与分布式场景的小心机
- 跨端接力:同一任务在手机→平板→中控屏连贯继续,状态同步用“分布式数据/对象”。
- 多屏一致:视觉与交互骨架一致,细节随场景适配(驾驶时减少文本输入)。
- 中断恢复:电话/语音导航来了又走,回来回到刚才那一帧。
设计—开发协作工具链(靠谱清单)
- DevEco Studio:ArkUI 预览、Layout Inspector、Profiler(布局/帧率/内存)。
- ArkUI Inspector:看组件树、边距、重绘边界。
- Token 化输出:设计端(Figma/Sketch)用插件导出色板/字号/间距→ 令牌文件(JSON/TS)。
- 图标规范:统一视口(24/32),对齐像素网格;浅/深模式双套。
- 动效曲线库:统一贝塞尔/时长,避免“每人来一口”。
可复用 UI 模板:卡片、底部栏、弹窗
卡片(信息密度友好)
@Component
struct InfoCard {
@Prop title: string
@Prop desc: string
@Prop icon: Resource
build() {
Row() {
Image(this.icon).width(32).height(32)
Column({ space: 6 }) {
Text(this.title).fontSize(18).fontWeight(FontWeight.Medium)
Text(this.desc).fontSize(14).fontColor('#6B7280')
}.margin({ left: 12 })
Blank()
Image($r('app.media.chev_right')).width(20).height(20)
}
.padding(16)
.backgroundColor('#fff')
.borderRadius(16)
.shadow({ radius: 12, color: 'rgba(0,0,0,0.06)' })
}
}
底部操作栏(大拇指友好)
// 48~56dp 高度,安全区适配
Row() {
Button('取消').type(ButtonType.Normal).layoutWeight(1)
Button('确定').type(ButtonType.Capsule).layoutWeight(1)
}.padding({ left: 16, right: 16, bottom: 16, top: 10 })
非打断式弹层(Sheet 替代硬弹窗)
@State showSheet: boolean = false
Button('选择城市').onClick(() => this.showSheet = true)
if (this.showSheet) {
Sheet() {
// 内容列表…
}
.height('60%')
.backgroundBlurStyle(BlurStyle.Thick) // 背景虚化
.onClose(() => this.showSheet = false)
}
评审清单(上线前逐条过!)
- 断网/弱网:骨架与错误页正常?
- 暗色模式:对比度、插图、阴影处理正常?
- 动效:入场≤300ms、切换≤200ms?
- 可访问性:热区≥44dp、读屏描述齐全?
- 性能:首屏 < 1s(冷启动骨架≤200ms),滚动稳定 60FPS?
- 分布式:接力/多端状态一致?
- 文案:动词优先、句子短、错误可恢复指引?
七天冲刺路线(给团队的排期模版)
- D1:场景梳理 → 用户旅程 → 信息架构 & 栅格断点
- D2:令牌落库(颜色/字号/间距/动效)→ 基础组件装配
- D3:关键流程(注册/搜索/下单…)线框 → 高保真
- D4:ArkUI 模板搭建(卡片/导航/表单/列表)
- D5:状态与动效(骨架/空状态/错误/过渡)
- D6:暗色/国际化/可访问性走查
- D7:弱网/异常/分布式接力联测 + 指标基线
常见“翻车瞬间”(都是血泪)
- 把颜色写死在组件里,导致暗色一换全项目“拉跨”。
- 列表 1000+ 项一次渲染,滑一下掉帧到你怀疑人生。
- 成功 Toast 一直挡按钮,用户连点三次。
- 表单只在提交时报错,用户填了 2 分钟被全清空。
- 弹窗滥用:二连弹 + 权限弹 + 评分弹,转化暴跌。
结语:UI 是“产品的语气”,UX 是“产品的态度”
在鸿蒙生态里做 UI/UX,比“画个好看的界面”难一点点,但也有章可循:令牌化 → 栅格与断点 → 状态与动效 → 可访问性 → 分布式协同。当你把这些“工程化”,审美就变成了“可复制的生产力”。🎯
你要的资料包(我可以继续补充)
- ArkUI 组件速查 & 模板片段
- 设计令牌 JSON/TS 样板(含浅/深)
- 骨架/空态/错误插画占位套件(浅/深双版)
- 性能与可访问性检查脚本(DevEco 配置说明)
…
(未完待续)
更多推荐



所有评论(0)