鸿蒙新特性:Tabs 标签页组件——构建设置中心与多视图导航
当应用的功能和信息超过一个屏幕能承载的范围时,就需要一种组织方式将内容分门别类。Tab 标签页是最经典的解决方案——从微信底部的"微信/通讯录/发现/我",到系统设置的"通用/显示/声音/隐私",标签页无处不在。它让用户一眼就能看到所有分类入口,点一次即可切换视图。
HarmonyOS NEXT ArkUI 提供了 Tabs 组件——一个完整的标签页容器,封装了标签栏渲染、内容区切换和滑动手势。本文将深入讲解 Tabs 组件的 API,并构建一个完整的"设置中心"应用——支持标签切换、开关控制、字体大小选择和缓存管理。
关键词:HarmonyOS、ArkUI、Tabs、标签页、设置中心、TabContent、自定义标签栏
一、Tabs 组件 API
1.1 基本用法
Tabs({ index: $$this.currentTab }) {
TabContent() {
// 第一个标签页的内容
}
.tabBar('标签一')
TabContent() {
// 第二个标签页的内容
}
.tabBar('标签二')
}
.onChange((index: number) => {
this.currentTab = index;
})
Tabs 由两个核心子组件构成:
- TabContent:单个标签页的内容容器,内部放置任意 UI
- tabBar:配置该标签页对应的标签栏显示内容
Tabs 本身通过 index 参数控制当前显示的标签页,$$ 双向绑定让 Tabs 和父组件的状态始终同步。
1.2 核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
index |
number | 当前选中标签页索引(0-based),支持 $$ 双向绑定 |
barMode |
BarMode | 标签栏模式:Fixed(固定宽度均分)或 Scrollable(可滚动) |
barWidth |
Length | 标签栏宽度,通常设为 '100%' |
barHeight |
Length | 标签栏高度 |
barPosition |
BarPosition | 标签栏位置:Start(顶部,默认)或 End(底部) |
vertical |
boolean | 是否为纵向标签页(true 时标签栏在左侧) |
onChange |
callback | 标签页切换回调,参数为新索引 |
1.3 BarMode 的选择
BarMode.Fixed 和 BarMode.Scrollable 的区别在于标签栏的空间分配:
- Fixed:所有标签等分标签栏宽度。适合 2-5 个标签,每个标签宽度 = 总宽 / 标签数。Demo 中的设置中心(3 个标签)使用 Fixed 模式。
- Scrollable:每个标签按内容自适应宽度,超出标签栏时支持横向滚动。适合 5 个以上的标签或标签文字长度不一的情况,如新闻分类、商品筛选。
1.4 自定义 tabBar
.tabBar() 可以接受字符串(简单文本标签),也可以接受 @Builder 函数(完全自定义的标签样式):
@Builder
tabBuilder(label: string, idx: number) {
Column() {
Text(label)
.fontColor(idx === this.currentTab ? '#1677FF' : '#888899')
.fontWeight(idx === this.currentTab ? FontWeight.Bold : FontWeight.Normal)
if (idx === this.currentTab) {
Divider()
.width(20)
.height(3)
.color('#1677FF')
.borderRadius(2)
}
}
}
// 使用
.tabBar(this.tabBuilder('通用设置', 0))
自定义 tabBar 的核心价值在于灵活控制选中/未选中状态的视觉差异。Demo 中使用"蓝色文字 + 底部短横线"表示选中,灰色文字表示未选中——这种风格常见于 Android Material Design 和国内主流 App。
1.5 $$ 双向绑定的机制
Tabs({ index: $$this.currentTab }) 中的 $$ 是 ArkUI 的双向绑定语法。它的工作原理是:
- 当用户在 UI 上点击标签或滑动切换时,Tabs 内部更新
currentTab - 当代码中修改
this.currentTab的值时,Tabs 自动切换到对应页面
这保证了用户手势和编程式控制(如"重置后回到第一个标签")始终一致。如果只使用单向绑定(不加 $$),则需要在 onChange 回调中手动同步状态。
二、设置中心的整体设计
2.1 页面架构
本文 Demo 构建一个"设置中心"应用,模拟系统设置的三级标签结构:
SettingsPage
├── 标题栏 — "设置" + 版本号
├── Tabs(标签页容器)
│ ├── Tab 1: "通用设置"
│ │ ├── 外观
│ │ │ ├── 深色模式(Toggle 开关)
│ │ │ └── 字体大小(小/中/大 三段选择器)
│ │ ├── 存储
│ │ │ └── 缓存管理(显示大小 + 清除按钮)
│ │ └── 关于
│ │ ├── 应用版本
│ │ ├── SDK 版本
│ │ └── 构建号
│ ├── Tab 2: "通知管理"
│ │ ├── 推送(推送通知 Toggle)
│ │ ├── 提醒方式(声音 + 震动 Toggle)
│ │ └── 通知历史(最近 3 条)
│ └── Tab 3: "隐私安全"
│ ├── 安全(生物识别 Toggle)
│ ├── 隐私(数据收集 Toggle)
│ ├── 权限管理(相机/麦克风/位置/通讯录 状态)
│ └── 协议与政策(用户协议 + 隐私政策 + 开源许可)
└── 底部固定栏 — "恢复默认设置"按钮
2.2 状态管理
设置中心有 8 个 @State 变量,覆盖三种类型:
@State currentTab: number = 0; // 当前标签索引
@State darkMode: boolean = false; // 布尔型设置:Toggle 开关
@State pushEnabled: boolean = true;
@State soundEnabled: boolean = true;
@State vibrationEnabled: boolean = false;
@State biometricsEnabled: boolean = false;
@State dataCollectionEnabled: boolean = true;
@State fontSizeIdx: number = 1; // 枚举型设置:字体大小(0/1/2)
@State cacheSize: string = '128 MB'; // 状态型:缓存大小文本
@State cacheCleared: boolean = false; // 布尔型:缓存是否已清除
所有状态变量由 Tabs、Toggle、按钮等组件通过回调函数修改。cacheCleared 是一个派生状态——它与 cacheSize 同步变化,用于控制"清除"按钮的禁用状态。
2.3 数据流向
用户操作 → Toggle.onChange / 按钮.onClick
→ @State 变量更新
→ UI 自动刷新(Toggle 位置、文字颜色、按钮状态)
以"深色模式"为例:
用户点击 Toggle → onChange(v: boolean)
→ this.darkMode = v
→ Toggle.isOn 更新(开关位置)
→ 无需额外操作
以"缓存清除"为例:
用户点击"清除" → clearCache()
→ this.cacheSize = '0 B'
→ this.cacheCleared = true
→ UI 更新:大小显示 '0 B',按钮变灰禁用

三、标签栏设计
3.1 自定义 tabBar Builder
每个标签使用 @Builder 函数定制样式:
@Builder
tabBuilder(label: string, idx: number) {
Column() {
Text(label)
.fontSize(14)
.fontColor(idx === this.currentTab ? '#1677FF' : '#888899')
.fontWeight(idx === this.currentTab ? FontWeight.Bold : FontWeight.Normal)
.padding({ top: 4, bottom: 4 })
if (idx === this.currentTab) {
Divider()
.width(20)
.height(3)
.color('#1677FF')
.borderRadius(2)
}
}
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.width('33.3%')
.height(44)
}
关键设计细节:
- 等宽分配:每个标签占
33.3%(3 个标签均分),与BarMode.Fixed配合使用 - 选中指示器:选中标签下方显示一个蓝色短横线(3px 高,20px 宽,2px 圆角)——这是 Material Design 风格的选中指示器
- 颜色区分:选中 = 蓝色加粗,未选中 = 灰色常规
- 固定高度:标签栏高度 44px,符合触控区域的最小推荐尺寸
3.2 标签栏配置
Tabs({ index: $$this.currentTab }) {
TabContent() { /* 内容 */ }.tabBar(this.tabBuilder('通用设置', 0))
TabContent() { /* 内容 */ }.tabBar(this.tabBuilder('通知管理', 1))
TabContent() { /* 内容 */ }.tabBar(this.tabBuilder('隐私安全', 2))
}
.barMode(BarMode.Fixed)
.barWidth('100%')
.barHeight(44)
BarMode.Fixed 配合 width('33.3%') 确保三个标签精确等分,不会随内容长度变化。标签高度 44px 是在可读性和空间效率之间的平衡。
四、通用设置标签页
4.1 深色模式开关
this.toggleRow('深色模式', '启用后使用深色主题,减少眼部疲劳',
this.darkMode,
(v: boolean) => { this.darkMode = v; })
toggleRow 是一个 @Builder 函数,接收标签、描述、当前值和回调函数:
@Builder
toggleRow(label: string, desc: string, value: boolean,
callback: (v: boolean) => void) {
Row() {
Column() {
Text(label).fontSize(15).fontColor('#1a1a2e').fontWeight(FontWeight.Medium)
Text(desc).fontSize(12).fontColor('#9999AA').margin({ top: 2 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: value })
.onChange((v: boolean) => { callback(v); })
}
}
Toggle 是 ArkUI 的开关组件,ToggleType.Switch 指定为滑动开关样式。每个设置项由左侧的文字说明和右侧的开关按钮组成,符合 iOS/Android 双端的设置页设计惯例。
4.2 字体大小选择器
字体大小使用三段选择器(小 / 中 / 大)而非 Toggle:
Row() {
ForEach(['小', '中', '大'], (label: string, idx: number) => {
Text(label)
.fontColor(idx === this.fontSizeIdx ? '#FFFFFF' : '#666677')
.fontWeight(idx === this.fontSizeIdx ? FontWeight.Bold : FontWeight.Normal)
.backgroundColor(idx === this.fontSizeIdx ? '#1677FF' : '#F8F9FA')
.borderRadius(16)
.onClick(() => { this.fontSizeIdx = idx; })
})
}
选中项蓝底白字,未选中灰底黑字——这种"三段分段控件"的视觉模式常见于 iOS 的设置页面。fontSizeIdx 是 0/1/2 的枚举值,默认值为 1(“中”)。
4.3 缓存管理
this.settingRowWithAction('缓存管理', this.cacheSize, '清除',
this.cacheCleared ? '#CCCCDD' : '#FF4D4F',
() => { if (!this.cacheCleared) { this.clearCache(); } })
settingRowWithAction 是一种"标签 + 数值 + 操作按钮"的布局模式。按钮颜色在"可清除"(红色 #FF4D4F)和"已清除"(灰色 #CCCCDD)之间变化,点击已清除状态的按钮不会有任何反应(通过 if (!this.cacheCleared) 守卫)。
4.4 关于信息
关于部分使用简单的"标签-值"配对展示不可编辑的应用信息:
this.infoRow('应用版本', '2.1.0')
this.infoRow('SDK 版本', 'API 24')
this.infoRow('构建号', '20260707')
五、通知管理标签页
通知管理标签页包含三个部分:
- 推送:推送通知的全局开关
- 提醒方式:声音和震动的独立开关——当用户关闭"推送通知"时,这两个开关应该变灰(Demo 中通过用户关闭推送后自然忽略声音/震动来实现)
- 通知历史:最近 3 条通知的标题、内容和时间,提供信息参考而非交互控件
@Builder
notificationItem(title: string, body: string, time: string) {
Row() {
Column() {
Text(title).fontSize(14).fontColor('#1a1a2e').fontWeight(FontWeight.Medium)
Text(body).fontSize(12).fontColor('#888899').margin({ top: 2 })
}
.layoutWeight(1)
Text(time).fontSize(11).fontColor('#CCCCDD')
}
}
六、隐私安全标签页
隐私安全标签页展示了三种不同的 UI 模式:
- Toggle 开关:生物识别、数据收集——标准的布尔设置
- 权限状态列表:相机/麦克风/位置/通讯录——只读状态展示,颜色区分授权级别:
- 已授权 → 绿色(
#52C41A) - 使用时询问 → 灰色(
#888899) - 未授权 → 红色(
#FF4D4F)
- 已授权 → 绿色(
- 可点击链接:用户协议、隐私政策、开源许可——蓝色文字 + 右箭头,暗示可点击跳转(Demo 中仅做展示)
@Builder
permissionRow(name: string, status: string) {
Row() {
Text(name).fontSize(15).fontColor('#1a1a2e')
Blank()
Text(status).fontSize(12)
.fontColor(status === '已授权' ? '#52C41A' :
(status === '未授权' ? '#FF4D4F' : '#888899'))
}
}
七、视图组织与导航
7.1 标签页内的 Scroll
每个 TabContent 内部使用 Scroll 包裹内容列,确保内容超出屏幕时可以上下滚动:
TabContent() {
Scroll() {
Column() {
// 设置项...
}
}
.layoutWeight(1)
.scrollBar(BarState.Off)
}
layoutWeight(1) 让 Scroll 占据 TabContent 的剩余空间(减去标签栏和底部按钮的高度),避免内容被截断。
7.2 分区标题
每个设置分区前有一个灰色小标题(如"外观"、“存储”、“关于”),通过 sectionHeader Builder 函数实现:
@Builder
sectionHeader(title: string) {
Text(title)
.fontSize(12)
.fontColor('#9999AA')
.fontWeight(FontWeight.Medium)
.padding({ left: 16, right: 16, top: 20, bottom: 4 })
}
分区标题在视觉上将设置项分组,用户在快速滚动时能通过标题定位当前位置。12px 灰色文字 + 最小字重——足够显眼但又不过分抢眼。
7.3 圆角卡片式布局
每个设置分区内的项目以"卡片组"的形式展示——整个分区是一个白色圆角矩形,内部通过分割线分隔不同的设置项:
Column() {
toggleRow(...) // 深色模式
// 如果需要分隔线,在 toggleRow 内部处理
}
.backgroundColor('#FFFFFF')
.borderRadius(BorderRadius.LG)
这种卡片式布局在 iOS 设置中最为典型,给每个功能组以清晰的视觉边界。
八、重置功能
8.1 恢复默认设置
底部固定栏有一个红色"恢复默认设置"按钮:
Row() {
Text('恢复默认设置')
.fontSize(14)
.fontColor('#FF4D4F')
.fontWeight(FontWeight.Medium)
}
.justifyContent(FlexAlign.Center)
.backgroundColor('#FFFFFF')
.onClick(() => { this.resetAll(); })
8.2 resetAll 实现
resetAll(): void {
this.darkMode = false;
this.fontSizeIdx = 1;
this.pushEnabled = true;
this.soundEnabled = true;
this.vibrationEnabled = false;
this.biometricsEnabled = false;
this.dataCollectionEnabled = true;
this.cacheSize = '128 MB';
this.cacheCleared = false;
this.currentTab = 0;
promptAction.showToast({ message: '已恢复默认设置', duration: 1500 });
}
所有 @State 变量被重置为初始值,currentTab 回到第一个标签页。同时显示 Toast 确认操作已生效。
这个功能的关键价值在于:它将"设置"从一个单向的配置记录升级为"可撤销的配置管理"——用户可以放心尝试不同的设置组合,知道随时可以通过一次点击恢复原状。
九、交互流程演示
9.1 浏览标签页
进入设置页面,默认显示"通用设置"标签。标签栏中"通用设置"为蓝色加粗 + 底部蓝色短横线,"通知管理"和"隐私安全"为灰色文字。
9.2 切换标签
点击"通知管理"标签。onChange 触发,currentTab 更新为 1。标签栏动画更新——"通知管理"变蓝,底部出现蓝色短横线,"通用设置"变灰、短横线消失。内容区切换到通知设置。
9.3 更改设置
在"通用设置"标签中,点击"深色模式"的 Toggle 开关。开关滑到右侧(开启位置),darkMode 变为 true。UI 即时响应——无需"保存"按钮。
在字体大小中选择"大"。fontSizeIdx 更新为 2,"大"按钮变为蓝底白字,"中"按钮恢复灰底黑字。同理,所有 Toggle 开关和按钮操作都是即时生效的。
9.4 清除缓存
点击缓存管理的"清除"按钮。cacheSize 从"128 MB"变为"0 B",“清除"按钮从红色变为灰色(cacheCleared = true),无法再次点击。Toast 提示"缓存已清除”。
9.5 恢复默认
点击底部"恢复默认设置"。所有 Toggle 回到初始状态、字体大小回到"中"、缓存大小恢复"128 MB"、“清除"按钮恢复红色。标签页回到第一个(通用设置)。Toast 提示"已恢复默认设置”。
十、总结
本文通过"设置中心"这个实战案例,全面讲解了 ArkUI Tabs 标签页组件的使用方法。核心知识点包括:
- Tabs + TabContent:标签页容器的基本结构,每个 TabContent 是一个独立的视图
- tabBar 自定义:通过
@Builder函数定制标签样式,实现选中/未选中状态的视觉差异 - $$ 双向绑定:
index状态与 Tabs 的双向同步,用户手势和编程式控制保持一致 - 标签页内布局:Scroll + Column + 圆角卡片,适应内容超出屏幕的情况
- 多种控件组合:Toggle 开关、三段选择器、操作按钮、只读信息、权限状态列表
- 全局重置:一键恢复所有设置为默认值 + 回到第一个标签
Tabs 是移动应用中最基础的导航组件之一。一个好的标签导航不仅仅是"点一下切换内容"——它需要清晰的选中指示器、平滑的切换动画、合理的标签数量(3-5 个)和直观的视觉层次。ArkUI 的 Tabs 组件提供了这些基础能力,而开发者需要在此基础上设计标签内容、交互反馈和状态管理策略。
更多推荐



所有评论(0)