前言
上一篇我们用Row+Spacer搭好了底部双按钮栏,但只是把按钮「摆上去」——按钮的视觉语义、圆角尺寸、点击反馈都还没讲透。这一篇专攻底部栏里那个灰色的「暂停」按钮,把Button的颜色系统、圆角档位、stateEffect点击反馈、buttonStyle视觉态四大要点一次讲透。
本篇以「猫猫大作战」底部暂停按钮为锚点。读完本篇你将能独立写出:主操作绿色按钮、次要操作灰色按钮、危险操作红色按钮三种视觉语义的 Button,并掌握圆角档位与点击反馈的搭配套路。
提示:本系列不讲 ArkTS 基础语法与环境搭建,假设你已跟完第 1–18 篇。本篇是 Button 组件的进阶样式篇。
一、场景拆解:暂停按钮的视觉规格
打开entry/src/main/ets/pages/Index.ets的GameView底部栏,暂停按钮代码如下:
// 来源:entry/src/main/ets/pages/Index.ets GameView() 底部栏 Button('暂停') .width(100) .height(44) .fontSize(15) .fontColor('#FFFFFF') .backgroundColor('#95A5A6') .borderRadius(22) .onClick(() => { this.pauseGame(); })这条按钮的视觉规格拆解:
| 属性 | 值 | 含义 |
|---|---|---|
width | 100 | 按钮宽 100vp |
height | 44 | 按钮高 44vp |
fontSize | 15 | 文字字号 15vp |
fontColor | #FFFFFF | 白色文字 |
backgroundColor | #95A5A6 | 灰色背景 |
borderRadius | 22 | 圆角 = height/2 = 胶囊形 |
关键经验:Button 的视觉规格 = 尺寸 + 圆角 + 颜色 + 字号四大要素,缺一不可。
二、Button 的颜色系统
2.1 色彩语义三档
ArkUI 没有强制按钮颜色规范,但业界有共识——按钮颜色 = 用户引导:
| 语义 | 颜色 | hex | 用途 | 本项目位置 |
|---|---|---|---|---|
| 主操作 | 成功绿 | #2ECC71 | 引导用户首选的操作 | 开始游戏按钮 |
| 次要操作 | 中性灰 | #95A5A6 | 不强推、随时可点 | 暂停按钮 |
| 危险操作 | 警告红 | #E74C3C | 不可逆、需谨慎 | 重新开始按钮 |
| 中性重置 | 提醒橙 | #F39C12 | 介于次要与危险之间 | 暂停弹窗的「重新开始」 |
提示:「重新开始」在底部栏是红色,在暂停弹窗里是橙色——底部栏的重新开始会清空进度(危险),暂停弹窗的重新开始同样清空进度,但用户已经在暂停态,意图更明确,所以用更温和的橙色。
2.2 字色对比度
.fontColor('#FFFFFF') // 白字 .backgroundColor('#95A5A6') // 灰底对比度公式:WCAG 建议正文与背景对比度 ≥ 4.5:1。本项目按钮配色对比度:
| 背景 | 字色 | 对比度 | 达标 |
|---|---|---|---|
灰#95A5A6 | 白#FFFFFF | 2.7:1 | ❌ 低于 4.5 |
红#E74C3C | 白#FFFFFF | 3.3:1 | ❌ |
绿#2ECC71 | 白#FFFFFF | 1.9:1 | ❌ |
踩坑:按 WCAG 严格标准,这些按钮都不达标。但大字号(≥18pt 或 14pt 粗体)放宽到 3:1,本项目按钮字号 15vp 不算大字。
实战经验:游戏类应用色彩对比度可适当放宽——视觉风格优先于无障碍标准。生产环境建议把灰底调深到#7F8C8D(对比度 4.6:1 达标)。
2.3 纯色 vs 渐变
// 写法 1:纯色(本项目) .backgroundColor('#95A5A6') // 写法 2:线性渐变(更立体) .linearGradient({ direction: GradientDirection.Bottom, colors: [['#BDC3C7', 0.0], ['#95A5A6', 1.0]] }) // 写法 3:深浅两段(最立体) .linearGradient({ direction: GradientDirection.Bottom, colors: [['#AAB7B8', 0.0], ['#95A5A6', 0.5], ['#7F8C8D', 1.0]] })| 方案 | 视觉 | 性能 |
|---|---|---|
| 纯色 | 平面 | 最优 |
| 渐变 | 立体 | 中等 |
| 三段渐变 | 最立体 | 略低 |
实战经验:移动端按钮建议纯色——渐变在小屏上立体感不明显,但消耗 GPU 填充率。本项目全部用纯色。
三、borderRadius 圆角档位
3.1 圆角档位表
ArkUI 的borderRadius是一个统一圆角值,不同档位视觉差异大:
| 圆角值 | 视觉 | 适用 | 本项目位置 |
|---|---|---|---|
| 0 | 直角 | 表格、卡片底 | — |
| 4 | 小圆角 | 输入框、Tag | — |
| 8 | 中圆角 | 卡片、面板 | — |
| 12 | 大圆角 | 棋盘背景 | Stack 棋盘 |
| 22(=height/2) | 胶囊形 | 按钮 | 暂停/重新开始按钮 |
| 999 / height | 超大圆角(=胶囊) | 头像、圆形按钮 | — |
关键公式:胶囊形圆角 = height / 2。本项目按钮 height=44,圆角=22,正好胶囊形。
3.2 圆角不一致的踩坑
// ❌ 错误:圆角 20 ≠ height/2=22,左右两端不是半圆 Button('暂停').height(44).borderRadius(20) // ✅ 正确:圆角 = height/2,左右两端是完美半圆 Button('暂停').height(44).borderRadius(22)视觉差异微小但可察觉——圆角 20 时两端略有「直角感」,22 时端到端是平滑半圆。
3.3 四角不同圆角
// 写法 1:统一圆角(本项目,推荐) .borderRadius(22) // 写法 2:四角分别设置 .borderRadius({ topLeft: 22, topRight: 22, bottomLeft: 22, bottomRight: 22 }) // 写法 3:只圆顶部两角(卡片标题区) .borderRadius({ topLeft: 12, topRight: 12, bottomLeft: 0, bottomRight: 0 })实战经验:统一圆角用数字写法(写法 1),四角不同才用对象写法(写法 2、3)。对象写法更冗长但可读性更好,团队协作时统一规范。
四、stateEffect 点击反馈
4.1 默认 stateEffect
Button('暂停') .backgroundColor('#95A5A6') // 默认 stateEffect=true:按下时背景色变浅 30%ArkUI 的Button有个stateEffect属性,默认true——按下时按钮会有视觉反馈(背景色微微变浅)。
| stateEffect | 按下视觉 | 适用 |
|---|---|---|
true(默认) | 背景变浅,有按压感 | 普通按钮 |
false | 无变化,按下「无感」 | 禁用按钮、加载中按钮 |
4.2 关闭 stateEffect 的场景
// 加载中按钮:禁用点击反馈 Button('加载中...').stateEffect(false).enabled(false) // 禁用按钮:灰化 + 无反馈 Button('不可用').stateEffect(false).backgroundColor('#ECF0F1').fontColor('#BDC3C7')实战经验:禁用按钮同时设stateEffect(false)+enabled(false)——前者去掉按压反馈,后者阻止点击事件。
4.3 自定义按压反馈
ArkUI 内置的stateEffect反馈较微弱,要更强反馈可监听 touch 事件改透明度:
Button('暂停') .backgroundColor('#95A5A6') .opacity(this.isPressed ? 0.7 : 1.0) // 按下 0.7,松开 1.0 .onTouch((event) => { if (event.type === TouchType.Down) this.isPressed = true if (event.type === TouchType.Up) this.isPressed = false })关键经验:简单按钮用默认 stateEffect,强反馈按钮用 opacity + onTouch 自定义。本系列第 30 篇会专讲 AttributeModifier 复用按钮样式。
五、buttonStyle 视觉态
5.1 ButtonStyle 枚举
ArkUI 的Button有三种buttonStyle(API 11+):
| ButtonStyle | 视觉 | 适用 |
|---|---|---|
NORMAL(默认) | 矩形/胶囊,纯色背景 | 通用操作按钮 |
CAPSULE | 强制胶囊形,自动 borderRadius | 移动端主操作 |
CIRCLE | 强制圆形(width=height) | 浮动按钮 FAB、图标按钮 |
// 写法 1:NORMAL + 手动 borderRadius=22(本项目等效) Button('暂停', { type: ButtonType.Normal }) .height(44).borderRadius(22) // 写法 2:CAPSULE 自动胶囊(最简) Button('暂停', { type: ButtonType.Capsule }) .height(44) // borderRadius 自动 = height/2 // 写法 3:CIRCLE 圆形按钮(FAB) Button('+', { type: ButtonType.Circle }) .width(56).height(56) .backgroundColor('#2ECC71')关键经验:胶囊按钮用ButtonType.Capsule最简——不用手算 borderRadius。本项目用ButtonType.Normal+ borderRadius=22 也能达到胶囊形,但 Capsule 更规范。
5.2 ButtonType 完整对照
// ButtonType.Normal:矩形,需手动设 borderRadius Button('A', { type: ButtonType.Normal }).borderRadius(8) // ButtonType.Capsule:胶囊形,自动圆角 Button('B', { type: ButtonType.Capsule }) // ButtonType.Circle:圆形,width=height 时自动圆 Button('C', { type: ButtonType.Circle }).width(48).height(48)| ButtonType | 自动圆角 | 需设 width/height | 视觉 |
|---|---|---|---|
| Normal | 否 | 是 | 矩形/圆角矩形 |
| Capsule | 是(=height/2) | 是 | 胶囊 |
| Circle | 是(=width/2) | 是(且 width=height) | 圆 |
六、按钮组合:底部栏双按钮配色
回顾底部栏完整按钮配置:
// 来源:entry/src/main/ets/pages/Index.ets GameView() 底部栏 Row() { // 暂停:灰色次要操作 Button('暂停') .width(100).height(44) .fontSize(15).fontColor('#FFFFFF') .backgroundColor('#95A5A6') .borderRadius(22) .onClick(() => { this.pauseGame(); }) Spacer() // 重新开始:红色危险操作 Button('重新开始') .width(100).height(44) .fontSize(15).fontColor('#FFFFFF') .backgroundColor('#E74C3C') .borderRadius(22) .onClick(() => { this.clearTimers(); this.startGame(); }) } .width('100%') .padding({ left: 24, right: 24, bottom: 24, top: 12 })两按钮差异表:
| 维度 | 暂停 | 重新开始 |
|---|---|---|
| 背景 | 灰#95A5A6 | 红#E74C3C |
| 含义 | 次要操作 | 危险操作 |
| 点击 | pauseGame | clearTimers + startGame |
| 失误成本 | 低(可继续) | 高(进度清零) |
七、完整代码:暂停按钮 + pauseGame
// 来源:entry/src/main/ets/pages/Index.ets pauseGame() { if (this.gameState === GameState.PLAYING) { this.gameState = GameState.PAUSED; } } // 底部栏暂停按钮(本篇重点) @Builder GameView() { Column() { this.GameHUD() Column() { /* 预告 + 棋盘 */ }.alignItems(HorizontalAlign.Center) Spacer() Row() { Button('暂停') .width(100).height(44) .fontSize(15).fontColor('#FFFFFF') .backgroundColor('#95A5A6') .borderRadius(22) .onClick(() => { this.pauseGame(); }) Spacer() Button('重新开始') .width(100).height(44) .fontSize(15).fontColor('#FFFFFF') .backgroundColor('#E74C3C') .borderRadius(22) .onClick(() => { this.clearTimers(); this.startGame(); }) } .width('100%') .padding({ left: 24, right: 24, bottom: 24, top: 12 }) } .width('100%').height('100%') .linearGradient({ direction: GradientDirection.Bottom, colors: [['#E8F4F8', 0.0], ['#D6EEF5', 0.5], ['#C9E8F2', 1.0]] }) .alignItems(HorizontalAlign.Center) }八、踩坑提示
8.1 Button 不设 width/height 会塌缩
// ❌ 错误:只设文字,不设尺寸 Button('暂停').fontSize(15) // Button 默认 wrap_content,宽 = 文字宽 + padding,高 = 文字高 + padding,很小 // ✅ 正确:显式设尺寸 Button('暂停').width(100).height(44)8.2 backgroundColor 不生效
// ❌ 错误:Button 用了 Capsule 类型但没设 backgroundColor,显示默认蓝 Button('暂停', { type: ButtonType.Capsule }) // 默认蓝主题色 // ✅ 正确:显式设 backgroundColor Button('暂停', { type: ButtonType.Capsule }) .backgroundColor('#95A5A6')8.3 borderRadius 与 ButtonType 冲突
// ❌ 错误:Capsule 自动圆角,再手动设 borderRadius 会冲突 Button('A', { type: ButtonType.Capsule }).borderRadius(10) // 实际渲染以 Capsule 自动圆角为准,borderRadius 被忽略 // ✅ 正确:Capsule 不设 borderRadius;要自定义圆角用 Normal Button('A', { type: ButtonType.Normal }).borderRadius(10)8.4 onClick 丢失 this
// ❌ 错误:普通函数 this 不指向组件 Button('暂停').onClick(function () { this.pauseGame() }) // ✅ 正确:箭头函数保留外层 this Button('暂停').onClick(() => { this.pauseGame() })本系列第 38 篇会专讲箭头函数 this 绑定。
九、调试技巧
- 按钮颜色看不到:检查 backgroundColor 是否被父容器的 backgroundColor 覆盖;检查 opacity 是否为 0。
- 按钮形状不对:Capsule 类型下别手动设 borderRadius,要自定义圆角改用 Normal。
- 点击无反馈:默认 stateEffect=true 会有按压反馈;若关闭了,可通过 onTouch 改 opacity 自定义反馈。
- 按钮太小点不到:HarmonyOS 建议最小可点击区域 48×48vp;本项目 100×44vp 宽度够,高度略低,可调到 48vp。
十、性能与最佳实践
- 胶囊按钮用 ButtonType.Capsule——自动圆角,不用手算 borderRadius。
- 颜色遵循语义——绿主操作、灰次要、红危险、橙中性。
- 对比度 ≥ 4.5:1——深底白字或浅底深字,避免中灰底白字。
- 禁用按钮双设——stateEffect(false) + enabled(false) 同时设。
- 强反馈用 opacity + onTouch——默认 stateEffect 反馈微弱。
- 最小点击区 48vp——低于 48vp 容易误触。
总结
本篇我们从暂停按钮样式切入,掌握了Button 颜色语义四档(绿/灰/红/橙)、borderRadius 胶囊形公式 = height/2、stateEffect 点击反馈与禁用、**buttonStyle 视觉态(Normal/Capsule/Circle)**四大要点,并给出了暂停按钮 + pauseGame 完整代码。核心要点:胶囊按钮用 Capsule 最简;颜色遵循语义;禁用按钮双设 stateEffect+enabled;最小点击区 48vp。
下一篇我们将拆解 SymbolGlyph——用系统符号字体替代 Emoji 的方案。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- 「猫猫大作战」项目源码:本仓库
entry/src/main/ets/pages/Index.ets - Button 组件官方指南
- ButtonType 枚举官方文档
- borderRadius 圆角属性官方指南
- ArkUI 无障碍设计最佳实践
- 开源鸿蒙跨平台社区
- HarmonyOS 开发者官方文档首页
- 系列索引:本仓库
articles/INDEX.md