HarmonyOS NEXT 企业级记账APP:实现底部 Tab 导航
实现底部 Tab 导航本文是《HarmonyOS NEXT 企业级开发实战30篇打造智能记账APP》系列的第04篇对应 Git Tagv0.0.4。承接第 03 篇的主题系统本篇使用 ArkUI 的 Tabs 组件搭建首页、统计、预算、我的四个底导航 Tab并封装 AppTabBar 通用组件。前言底导航 Tab 是移动应用的核心导航模式用户 80% 的操作都从底导航发起。HarmonyLedger 规划四个主 Tab首页、统计、预算、我的。ArkUI 提供原生Tabs组件支持底导航但企业级项目需要封装统一的 AppTabBar 组件便于后续扩展与主题适配。本文将带你使用 ArkUI Tabs 组件搭建底导航框架封装 AppTabBar 通用组件支持图标、文字、主题色实现四个 Tab 页面占位与切换动效集成主题系统响应深色模式企业级核心原则底导航必须封装复用、禁止重复、支持主题。参考 ArkUI Tabs 组件 了解官方约定。一、Tab 规划1.1 四个主 TabHarmonyLedger 选用最经典的四 Tab布局覆盖核心业务场景Tab 序号名称图标页面业务0首页homeHomeView账单汇总、快捷新增1统计chartStatisticsView收支图表分析2预算budgetBudgetView预算设置与进度3我的userProfileView设置、关于1.2 Tab 命名规范Tab 命名规范 - 英文小写下划线home / chart / budget / user - 中文展示用首页 / 统计 / 预算 / 我的 - 图标资源icon_tab_{name}.png - 路由页面{Name}View.ets大驼峰 - 资源字符串tab_{name}_label设计要点Tab 命名一旦确定禁止修改否则会牵动图标、路由、字符串多处资源。二、Tabs 组件基础2.1 Tabs 与 TabContentArkUI 原生 Tabs 组件使用方式Entry Component struct MainView { State currentIndex: number 0; build() { Tabs({ index: this.currentIndex }) { TabContent() { HomeView() } .tabBar({ text: 首页, icon: $r(app.media.icon_tab_home) }) TabContent() { StatisticsView() } .tabBar({ text: 统计, icon: $r(app.media.icon_tab_chart) }) TabContent() { BudgetView() } .tabBar({ text: 预算, icon: $r(app.media.icon_tab_budget) }) TabContent() { ProfileView() } .tabBar({ text: 我的, icon: $r(app.media.icon_tab_user) }) } .barPosition(BarPosition.End) .onChange((index: number) { this.currentIndex index; }) } }2.2 关键属性属性说明值barPositionTab 位置BarPosition.End底导航/Start顶导航vertical是否纵向false默认横向scrollable可滚动false四 Tab 不需滚动onChange切换回调(index: number) voidtabBarTab 标签配置{ text, icon }踩坑点TabContent不能直接传 builder 参数必须用.tabBar(...)设置标签。这是鸿蒙 ArkUI 的强制约束。三、封装 AppTabBar3.1 组件设计目标企业级底导航需要满足图标文字双显示未选中态灰色、选中态主题色响应深色模式颜色自动切换Badge 角标支持消息提醒可配置Tab 数量与内容3.2 TabConfig 数据模型// model/TabConfig.ets export class TabConfig { name: string; // home / chart / budget / user label: string; // 显示文字 icon: Resource; // 图标资源 badge?: number 0; // 角标数字0 不显示 constructor(name: string, label: string, icon: Resource, badge?: number) { this.name name; this.label label; this.icon icon; this.badge badge ?? 0; } }3.3 AppTabBar 组件实现// components/navigation/AppTabBar.ets import { TabConfig } from ../../model/TabConfig; import { AppColors } from ../../theme/Colors; import { AppFontSize } from ../../theme/Typography; import { AppSpace } from ../../theme/Spacing; Component export struct AppTabBar { Prop tabs: TabConfig[] []; Prop currentIndex: number 0; BuilderParam onTabClick: (index: number) void; build() { Row() { ForEach(this.tabs, (tab: TabConfig, index: number) { Column() { Image(tab.icon) .width(24) .height(24) .fillColor(this.currentIndex index ? AppColors.Budget : AppColors.SecondaryText) Text(tab.label) .fontSize(AppFontSize.XS) .fontColor(this.currentIndex index ? AppColors.Budget : AppColors.SecondaryText) .margin({ top: AppSpace.XS }) // Badge 角标 if (tab.badge 0) { Text(tab.badge.toString()) .fontSize(10) .fontColor(#FFFFFF) .backgroundColor(AppColors.Expense) .borderRadius(8) .padding(2) .position({ x: 24, y: -4 }) } } .layoutWeight(1) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .onClick(() { this.onTabClick(index); }) }, (tab: TabConfig) tab.name) } .width(100%) .height(56) .backgroundColor(AppColors.CardBackground) .border({ width: { top: 1 }, color: AppColors.Separator }) } }关键技术BuilderParam用于将点击回调暴露给父组件这是 ArkUI V1 组件通信的标准方案。四、四个 Tab 页面占位4.1 HomeView 占位// pages/HomeView.ets Entry Component struct HomeView { build() { Column() { Text(首页) .fontSize(24) .fontWeight(FontWeight.Bold) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) } }4.2 StatisticsView 占位// pages/StatisticsView.ets Entry Component struct StatisticsView { build() { Column() { Text(统计分析) .fontSize(24) .fontWeight(FontWeight.Bold) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) } }4.3 BudgetView 与 ProfileView// pages/BudgetView.ets Entry Component struct BudgetView { build() { Column() { Text(预算中心) .fontSize(24) .fontWeight(FontWeight.Bold) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) } } // pages/ProfileView.ets Entry Component struct ProfileView { build() { Column() { Text(我的) .fontSize(24) .fontWeight(FontWeight.Bold) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) } }五、主页面集成5.1 MainView 集成 Tabs// pages/MainView.ets import { AppTabBar } from ../components/navigation/AppTabBar; import { TabConfig } from ../model/TabConfig; import { HomeView } from ./HomeView; import { StatisticsView } from ./StatisticsView; import { BudgetView } from ./BudgetView; import { ProfileView } from ./ProfileView; Entry Component struct MainView { State currentIndex: number 0; State tabs: TabConfig[] [ new TabConfig(home, 首页, $r(app.media.icon_tab_home)), new TabConfig(chart, 统计, $r(app.media.icon_tab_chart)), new TabConfig(budget, 预算, $r(app.media.icon_tab_budget)), new TabConfig(user, 我的, $r(app.media.icon_tab_user)) ]; build() { Stack({ alignContent: Alignment.TopStart }) { // 内容区 Column() { if (this.currentIndex 0) { HomeView() } else if (this.currentIndex 1) { StatisticsView() } else if (this.currentIndex 2) { BudgetView() } else { ProfileView() } } .width(100%) .height(100%) .padding({ bottom: 56 }) // 底导航 AppTabBar({ tabs: this.tabs, currentIndex: this.currentIndex, onTabClick: (index: number) { this.currentIndex index; } }) } .width(100%) .height(100%) } }5.2 路由配置更新// entry/src/main/resources/base/profile/main_pages.json { src: [ pages/MainView, // 主入口改为 MainView pages/HomeView, pages/StatisticsView, pages/BudgetView, pages/ProfileView ] }// EntryAbility.ets 入口改为 MainView windowStage.loadContent(pages/MainView, (err) { ... });架构调整入口从 HomeView 改为 MainView底导航由 MainView 持有四个 Tab 页面作为子组件。这是鸿蒙多 Tab 应用的标准结构。六、图标资源准备6.1 Tab 图标规格项规格尺寸24x24 dp格式PNG带 alpha 通道或 SVG色彩单色通过 fillColor 染色命名icon_tab_{name}.png6.2 资源放置entry/src/main/resources/base/media/ ├─ icon_tab_home.png ├─ icon_tab_chart.png ├─ icon_tab_budget.png └─ icon_tab_user.png统一染色方案图标统一用白色 PNG通过Image.fillColor()动态染色。这样未选中态、选中态、深色模式都能复用同一张图。七、最佳实践7.1 Tab 切换性能优化// ❌ 错误每次切换都重建所有 Tab 页面 Tabs() { TabContent() { HomeView() } TabContent() { StatisticsView() } // 切换到统计时HomeView 仍在后台占用内存 } // ✅ 正解用 if 懒加载 Stack() { if (this.currentIndex 0) { HomeView() } else if (this.currentIndex 1) { StatisticsView() } }7.2 Tab 状态保持如果希望 Tab 切换时保留各页面状态State homeState: HomeState new HomeState(); State statsState: StatsState new StatsState(); // 用 Stack 叇叠所有页面通过 visibility 控制 Stack() { HomeView({ state: this.homeState }).visibility(this.currentIndex 0 ? Visibility.Visible : Visibility.Hidden) StatisticsView({ state: this.statsState }).visibility(this.currentIndex 1 ? Visibility.Visible : Visibility.Hidden) }权衡状态保持会占用更多内存HarmonyLedger 暂不启用后续 v0.2.7 性能优化篇再讨论。八、运行验证8.1 编译检查hvigorw assembleHap--modemodule-pproductdefault8.2 运行验证应用启动后显示首页底导航四个 Tab 均可见点击统计 Tab切换到统计分析页选中态 Tab 文字和图标变蓝色AppColors.Budget未选中态 Tab 灰色深色模式下同步切换九、常见问题9.1 TabContent 传参错误错误原因解决TabContent(builder)不渲染不能传 builder 对象用TabContent() { ... }包裹内容图标不显示资源路径错误检查$r(app.media.icon_tab_xxx)名是否一致9.2 切换闪烁// 原因if 切换会销毁重建页面 // 解决用 visibility 保留所有页面 .visibility(currentIndex index ? Visibility.Visible : Visibility.Hidden)9.3 底部遮挡// 内容区需预留底导航高度 Column() { ... } .padding({ bottom: 56 }) // 与 AppTabBar.height 一致十、Git 提交10.1 Commit Messagegitadd.gitcommit-mfeat(navigation): 实现底部 Tab 导航与 AppTabBar 封装 - 新增 AppTabBar 通用底导航组件 - 新增 TabConfig 数据模型 - 创建 HomeView/StatisticsView/BudgetView/ProfileView 占位页 - 集成 MainView 持有 Tabs 切换 - 更新 EntryAbility 入口为 MainView - 添加四个 Tab 图标资源10.2 CHANGELOG## [v0.0.4] - 2026-07-27 ### Added - components/navigation/AppTabBar.ets通用底导航组件 - model/TabConfig.etsTab 配置数据模型 - pages/MainView.ets主页面持有 Tabs - pages/StatisticsView.ets、BudgetView.ets、ProfileView.ets占位页 - 资源icon_tab_home/chart/budget/user.png ### Changed - EntryAbility 入口改为 MainView - main_pages.json 增加 MainView 路由附录运行效果截图上图展示了本模块的完整运行效果。实际项目中可使用 DevEco Studio 模拟器或真机截图替换此占位图。总结本文完整介绍了 HarmonyLedger底部 Tab 导航的实现包含原生 Tabs 用法、AppTabBar 组件封装、四个 Tab 页面占位、主页面集成。通过本篇你可以使用 ArkUI Tabs 组件搭建底导航封装可复用的 AppTabBar 组件支持图标文字角标实现主题色响应选中态与深色模式理解 EntryAbility 与 MainView 的入口关系下一篇预告《首页布局与 SummaryCard 设计》将开发首页核心 UI今日支出、今日收入、最近账单列表并封装 SummaryCard 汇总卡片组件。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源本篇源码GitHub Tag v0.0.4ArkUI Tabs 文档tabsArkUI Image 组件imageTabContent APItabcontentArkUI 组件通信component-communicationBarPosition 枚举barposition鸿蒙图标资源规范icon-resources

相关新闻

混合云架构下网络互通方案设计

混合云架构下网络互通方案设计

混合云架构下网络互通方案设计随着企业数字化转型的深入,混合云凭借其兼顾公有云弹性灵活与私有云安全可控的独特优势,已成为主流IT架构选择。然而,混合云的核心价值在于“混合”,即公有云与私有云、边缘节点乃至多云环境之间能否…

2026/10/29 5:56:40 阅读更多 →
嵌入式领域AI4SE详解:AI赋能软件工程的实践与展望

嵌入式领域AI4SE详解:AI赋能软件工程的实践与展望

1. 引言:嵌入式软件开发的挑战与AI机遇嵌入式系统作为现代工业、汽车、医疗和消费电子的核心,其软件开发面临着独特的挑战:资源受限(内存、算力)、实时性要求高、安全性与可靠性至关重要、软硬件耦合紧密、开发周期长且…

2026/10/25 23:17:52 阅读更多 →
AcWing 多重背包问题I

AcWing 多重背包问题I

问题如下: 有 N种物品和一个容量是 V的背包。 第 i 种物品最多有 si件,每件体积是 vi,价值是 wi。 求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 输出最大价值。 输入格式 第一行两个整数…

2026/10/24 22:33:11 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/31 0:19:11 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/31 10:20:19 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/31 10:20:23 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/31 0:59:28 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/31 10:30:19 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/31 9:29:43 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/31 18:03:41 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/31 11:00:40 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/31 0:09:01 阅读更多 →