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/7/29 2:22:18阅读更多 →
Arduino与Processing串口通信实战:从传感器数据到动态可视化

Arduino与Processing串口通信实战:从传感器数据到动态可视化

1. 项目概述:当硬件遇见视觉如果你玩过Arduino,那你一定体验过点亮LED、驱动舵机、读取传感器数据的乐趣。但你是否想过,这些来自物理世界的“信号”能否驱动屏幕上那些酷炫的动画、生成一段音乐,甚至创造一个交互式艺术装置&…

2026/7/29 2:20:17阅读更多 →
BOSS 直聘仿写项目实战:管理端企业列表与认证审核(前后端闭环)

BOSS 直聘仿写项目实战:管理端企业列表与认证审核(前后端闭环)

本文是 Boss-API 系列的「企业审核篇」,承接前两篇(三端架构总览、求职者管理与忘记密码)。前两篇把「人」侧的业务跑通了;本篇聚焦「企业」侧最核心的一块:管理端怎么拉企业列表、怎么审核认证、审核结果又怎么回流到…

2026/7/29 2:20:17阅读更多 →
SpringBoot漫画阅读网站毕业设计实战指南

SpringBoot漫画阅读网站毕业设计实战指南

1. 项目背景与核心需求这个SpringBoot漫画阅读网站的设计初衷,是为计算机专业学生提供一个完整的毕业设计参考方案。从技术选型来看,它采用了当前企业级开发中最主流的SpringBoot框架,结合了漫画阅读这个具有明确用户场景的垂直领域。为什么选…

2026/7/29 3:32:31阅读更多 →
阿里云OSS大文件直传实战:STS临时凭证避坑与前端分片上传优化

阿里云OSS大文件直传实战:STS临时凭证避坑与前端分片上传优化

1. 项目概述:一次由STS临时凭证引发的“血案” 最近在重构一个内部文件管理系统时,我遇到了一个相当典型却又容易踩坑的场景:使用阿里云OSS的对象存储服务,通过后台签发STS(Security Token Service)临时凭…

2026/7/29 3:32:31阅读更多 →
Spring @Autowired注解原理深度解析:从依赖注入到BeanPostProcessor

Spring @Autowired注解原理深度解析:从依赖注入到BeanPostProcessor

1. 项目概述:从“自动注入”到“依赖查找”的魔法在Spring框架的日常开发中,Autowired注解几乎是我们每天都要打交道的“老朋友”。我们习惯于在字段、构造器或者Setter方法上随手加上一个Autowired,然后Spring容器就会像变魔术一样&#xff…

2026/7/29 3:32:31阅读更多 →
Matlab高级绘图:从对象模型到出版级图表制作全攻略

Matlab高级绘图:从对象模型到出版级图表制作全攻略

1. 项目概述:为什么你的Matlab图总是不够“高级”?每次看到别人论文里那些清晰、美观、信息量爆炸的Matlab图,再看看自己画出来的那些默认样式、配色辣眼、排版混乱的“草图”,是不是总感觉差了点什么?我用了十多年Mat…

2026/7/29 3:32:31阅读更多 →
Java分布式事务JTA实战:从核心原理到Spring Boot多数据源应用

Java分布式事务JTA实战:从核心原理到Spring Boot多数据源应用

1. 项目概述:为什么分布式事务是Java开发者的必修课? 如果你正在开发一个微服务架构的电商系统,用户下单这个动作,背后可能涉及订单服务创建订单、库存服务扣减库存、账户服务冻结余额、积分服务增加积分。这四个操作分别在不同的…

2026/7/29 3:32:31阅读更多 →
CTFshow Web477-479实战:文件上传、包含与命令执行漏洞剖析

CTFshow Web477-479实战:文件上传、包含与命令执行漏洞剖析

1. 项目概述:一次聚焦CMS核心漏洞的实战演练最近在CTFshow平台上刷题,从Web477到Web479这三道题,可以说是把CMS(内容管理系统)里几个最经典、也最要命的漏洞类型给串起来了。很多刚入门Web安全的朋友,一听到…

2026/7/29 3:30:31阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/28 4:06:39阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/28 2:08:06阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/28 1:38:28阅读更多 →
28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“! 在构建复杂的 Agent 系统时,我们经常会遇到这样的场景:Agent 正在执行一个多步骤的任务,比如“下单购买商品”,但执行到一半时,我们…

2026/7/29 0:01:46阅读更多 →
自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

近日,国际专注开放式技术研发的声学品牌Nank南卡,正式官宣实力艺人曾舜晞担任品牌代言人。消息一经发出便轰动全网。为什么耳机品牌不选择流量明星、老牌歌手?而且是选择曾舜晞?让我们一起来探索一下!比起短期的流量&a…

2026/7/29 0:01:46阅读更多 →
【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:01:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/28 20:22:24阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/28 3:17:03阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/28 2:35:58阅读更多 →