HarmonyOS应用开发实战(基础篇)Day12 -《打造专业级底部导航栏》
本文将通过一个完整示例,详解如何构建一个带图标与文字的底部 TabBar,并深入剖析其背后的响应式机制与工程化设计思想。

打造专业级底部导航栏:HarmonyOS ArkTS 中 Tabs 与 @Builder 的完美结合
在移动端应用开发中,底部标签栏(TabBar) 是用户导航的核心入口。它不仅需要清晰的视觉反馈(选中/未选中状态),还需支持图标、文字、点击切换等交互逻辑。在 HarmonyOS 的 ArkTS 开发体系中,Tabs 组件配合 @Builder 自定义构建器,为我们提供了一种简洁、高效且高度可定制的实现方案。
本文将通过一个完整示例,详解如何构建一个带图标与文字的底部 TabBar,并深入剖析其背后的响应式机制与工程化设计思想。
一、效果预览与需求分析

我们希望实现如下功能:
- 底部显示 4 个 Tab:我的、首页、更多、发现;
- 每个 Tab 包含 图标 + 文字;
- 选中项使用高亮图标与蓝色文字,未选中项使用灰色图标与文字;
- 点击 Tab 切换对应页面内容;
- TabBar 固定在屏幕底部(符合移动端习惯)。
最终效果如下(模拟图):
[ 首页内容 ]
------------------
🏠 首页 🔍 发现 ⋮ 更多 👤 我的
(“首页”为蓝色高亮)
二、核心实现:Tabs + @Builder
1. 页面结构骨架
@Entry
@Component
struct Index {
@State currentIndex: number = 0;
build() {
Column() {
Tabs({
barPosition: BarPosition.End, // 关键:TabBar 放在底部
index: this.currentIndex // 控制当前激活的 Tab
}) {
// TabContent 定义每个页面
}
.onChange((index: number) => {
this.currentIndex = index; // 同步状态
})
.width('100%')
.height('100%')
}
}
}
@State currentIndex:记录当前选中的 Tab 索引,是 UI 响应式更新的核心;barPosition: BarPosition.End:将 TabBar 渲染在容器末尾,在垂直布局中即为底部;.onChange:监听 Tab 切换事件,更新currentIndex,形成数据闭环。
💡 为什么用
@State?
因为 Tab 图标和文字的颜色/资源依赖于currentIndex,只有将其声明为响应式状态,UI 才能在切换时自动刷新。
2. 自定义 TabBar:@Builder 的妙用
直接在 tabBar() 中写 UI 会导致代码冗余。为此,我们使用 @Builder 抽取通用 Tab 项:
@Builder
tabBuilder(title: ResourceStr, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
Column() {
Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
.size({ width: 25, height: 25 })
Text(title)
.fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
}
.width('100%')
.height(50)
.justifyContent(FlexAlign.Center)
}
参数说明:
| 参数 | 类型 | 作用 |
|---|---|---|
title |
ResourceStr |
支持 $r('app.string.xxx') 国际化文本 |
targetIndex |
number |
该 Tab 对应的索引(用于判断是否选中) |
selectedImg |
Resource |
选中状态图标(如 mine_selected) |
normalImg |
Resource |
默认状态图标(如 mine) |
响应式逻辑:
- 图标切换:
this.currentIndex === targetIndex ? selectedImg : normalImg - 文字变色:选中时
#1698CE(品牌蓝),否则#6B6B6B(灰色)
✅ 优势:
- 避免重复代码(4 个 Tab 共用同一套 UI 逻辑);
- 修改样式只需调整
tabBuilder内部;- 支持任意数量 Tab 扩展。
3. 资源管理:图标与文本
HarmonyOS 推荐将静态资源(图片、字符串)放入 resources 目录:
resources/
├── base/
│ ├── element/
│ │ └── string.json # 文本资源
│ └── media/ # 图片资源
│ ├── mine.png
│ ├── mine_selected.png
│ ├── index.png
│ └── ...
在代码中通过 $r() 引用:
$r('app.media.mine') // 图片
$r('app.string.tab_mine') // 文本(若使用 string.json)
📌 提示:
示例中title直接传字符串(如'我的'),但生产环境建议使用$r('app.string.xxx')以支持多语言。
4. 组装 TabContent
每个页面用 TabContent 包裹,并通过 .tabBar() 绑定自定义 UI:
TabContent() {
Column() { Text('这是我的页面') }
.width('100%').height('100%')
}
.tabBar(this.tabBuilder('我的', 0, $r('app.media.mine_selected'), $r('app.media.mine')))
- 索引对齐:
targetIndex必须与 Tab 在Tabs中的物理顺序一致(从 0 开始); - 内容隔离:每个
TabContent内部可嵌套复杂页面逻辑,互不影响。
三、关键机制解析
1. 数据流闭环
整个过程由 @State 驱动,无需手动操作 DOM,符合声明式 UI 范式。
2. 为什么不用独立组件?
你可能会问:“能否把 Tab 项做成 @Component?”
可以,但不必要。原因如下:
- Tab 项无内部状态,仅依赖父组件数据;
@Builder零性能开销(无组件实例化);- 所有逻辑集中在同一文件,维护更简单。
✅ 经验法则:
若 UI 片段仅在当前组件内复用,优先用@Builder;若需跨页面共享,再考虑独立组件。
四、扩展与优化建议
1. 添加 Badge(红点提示)
在 tabBuilder 的 Image 外层包裹 Badge:
Badge() {
Image(...)
}.count(targetIndex === 0 ? 3 : 0) // 仅“我的”显示红点
2. 动画过渡
为图标切换添加淡入淡出效果:
Image(...)
.opacity(this.currentIndex === targetIndex ? 1 : 0.6)
.transition(TransitionEffect.fade())
3. 路由解耦
当前所有页面写在 Index 中,实际项目应拆分为独立页面组件:
// pages/HomePage.ets
@Entry
@Component
struct HomePage { /* ... */ }
// 在 TabContent 中引用
TabContent() { HomePage() }
4. 性能优化
- 使用
LazyForEach替代ForEach(若 Tab 内容为长列表); - 图标资源使用 SVG 或 小尺寸 PNG,减少内存占用。
五、总结
通过 Tabs + @Builder 的组合,我们以极简代码实现了专业级底部导航栏,充分体现了 ArkTS 的三大优势:
- 声明式语法:UI 由状态驱动,逻辑清晰;
- 高效复用:
@Builder避免重复代码; - 深度定制:完全掌控 TabBar 的每一像素。
🚀 进阶方向:
- 结合
router实现页面跳转;- 使用
@StorageLink持久化 Tab 状态;- 适配深色模式(Dark Mode)。
掌握这一模式,你不仅能快速构建 Tab 导航,更能举一反三,应用于顶部分类栏、设置菜单、商品筛选等各类场景,真正释放 HarmonyOS UI 开发的生产力!
附:完整代码已开源
👉 [AtomGit链接]
欢迎在评论区交流你的 TabBar 实现技巧!
更多推荐

所有评论(0)