当应用的功能和信息超过一个屏幕能承载的范围时,就需要一种组织方式将内容分门别类。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.FixedBarMode.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 的双向绑定语法。它的工作原理是:

  1. 当用户在 UI 上点击标签或滑动切换时,Tabs 内部更新 currentTab
  2. 当代码中修改 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')

五、通知管理标签页

通知管理标签页包含三个部分:

  1. 推送:推送通知的全局开关
  2. 提醒方式:声音和震动的独立开关——当用户关闭"推送通知"时,这两个开关应该变灰(Demo 中通过用户关闭推送后自然忽略声音/震动来实现)
  3. 通知历史:最近 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 模式:

  1. Toggle 开关:生物识别、数据收集——标准的布尔设置
  2. 权限状态列表:相机/麦克风/位置/通讯录——只读状态展示,颜色区分授权级别:
    • 已授权 → 绿色(#52C41A
    • 使用时询问 → 灰色(#888899
    • 未授权 → 红色(#FF4D4F
  3. 可点击链接:用户协议、隐私政策、开源许可——蓝色文字 + 右箭头,暗示可点击跳转(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 标签页组件的使用方法。核心知识点包括:

  1. Tabs + TabContent:标签页容器的基本结构,每个 TabContent 是一个独立的视图
  2. tabBar 自定义:通过 @Builder 函数定制标签样式,实现选中/未选中状态的视觉差异
  3. $$ 双向绑定index 状态与 Tabs 的双向同步,用户手势和编程式控制保持一致
  4. 标签页内布局:Scroll + Column + 圆角卡片,适应内容超出屏幕的情况
  5. 多种控件组合:Toggle 开关、三段选择器、操作按钮、只读信息、权限状态列表
  6. 全局重置:一键恢复所有设置为默认值 + 回到第一个标签

Tabs 是移动应用中最基础的导航组件之一。一个好的标签导航不仅仅是"点一下切换内容"——它需要清晰的选中指示器、平滑的切换动画、合理的标签数量(3-5 个)和直观的视觉层次。ArkUI 的 Tabs 组件提供了这些基础能力,而开发者需要在此基础上设计标签内容、交互反馈和状态管理策略。


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐