🚀在鸿蒙应用开发中,底部导航栏 TabBar 是使用频率最高的组件之一。
很多人只用默认样式,其实 ArkUI 提供了更强的定制能力------我们可以完全自定义样式、选中状态、甚至中间凸起的特殊按钮。
本篇就带你从「官方结构」到「自定义高亮控制」「class 类机制」的完整实现,一步步写出像小米商城一样精致的底部导航栏。
目录
[🧩一、TabBar 的基础逻辑与结构](#🧩一、TabBar 的基础逻辑与结构)
[🎨二、自定义 TabBar 的核心思路](#🎨二、自定义 TabBar 的核心思路)
[🔧1️⃣ 定义状态与构建函数](#🔧1️⃣ 定义状态与构建函数)
[⚙️三、Tabs 事件监听与状态更新](#⚙️三、Tabs 事件监听与状态更新)
[🧠五、进阶拓展:TabBar 动效与图标联动](#🧠五、进阶拓展:TabBar 动效与图标联动)
[🧱六、TypeScript 中的 class 类机制详解](#🧱六、TypeScript 中的 class 类机制详解)
[例 1:Person 类](#例 1:Person 类)
[例 2:Cat 类](#例 2:Cat 类)
[例:Food 类(直接传参)](#例:Food 类(直接传参))
[例:Food 类(结合接口传参)](#例:Food 类(结合接口传参))
[例:Person 类的 sayHi 方法](#例:Person 类的 sayHi 方法)
[❓七、常见问题 Q&A](#❓七、常见问题 Q&A)
🧩一、TabBar 的基础逻辑与结构
在 ArkUI 中,Tabs 与 TabContent 组件组合使用,就能实现基础的多页切换。
简单结构如下👇:

TypeScript
Tabs() {
TabContent() {
Text('首页内容')
}.tabBar('首页')
TabContent() {
Text('购物车内容')
}.tabBar('购物车')
}

✅ 每个 TabContent 对应一个页面。
✅ .tabBar() 定义页签标题或自定义内容。
🎨二、自定义 TabBar 的核心思路
默认 TabBar 的样式往往不能满足需求,比如想让选中状态显示红色、图标变化、或添加动画效果。
这时我们可以借助 @Builder 来自定义 TabBar 外观。
🔧1️⃣ 定义状态与构建函数

TypeScript
@State selectedIndex: number = 0
@Builder
myBuilder(itemIndex: number, title: string, img: ResourceStr, selImg: ResourceStr) {
Column() {
// 图标根据选中状态切换
Image(this.selectedIndex === itemIndex ? selImg : img)
.width(30)
.height(30)
.opacity(this.selectedIndex === itemIndex ? 1 : 0.7)
// 文字颜色动态变化
Text(title)
.fontColor(this.selectedIndex === itemIndex ? Color.Red : Color.Black)
.fontSize(12)
}
}
💡关键点:
• 通过 @State 管理当前激活索引
• 使用三元表达式控制图标和颜色
• 结合 .opacity() 可增强点击的动效感
⚙️三、Tabs 事件监听与状态更新
要让切换真正起作用,必须监听 Tabs 的切换事件。


|-----------------|-------------------|---------------|
| 事件 | 说明 | 参数 |
| onChange() | 页面切换后触发(滑动、点击均触发) | index(当前页索引) |
| onTabBarClick() | 点击页签时触发 | index(被点击页索引) |
示例:
TypeScript
Tabs() {
TabContent() {
Text('购物车内容')
}
.tabBar(this.myBuilder(2, '购物车', $r('app.media.ic_tabbar_icon_2'), $r('app.media.ic_tabbar_2_selected')))
.onChange((index: number) => {
console.log('当前激活索引:', index)
this.selectedIndex = index
})
}
✅ onChange 更适合逻辑绑定,比如刷新数据或更新选中态;
✅ onTabBarClick 可实现拦截操作,如未登录提示跳转。
💎四、手机商城式底部导航实战:中间凸起特效
现在我们来实现一个类似「手机商城」风格的底部导航栏。
中间有一个凸起的活动按钮,左右两边是常规页面。
TypeScript
@State selectedIndex: number = 0
@Builder
CenterBuilder() {
// 中间凸起的图标
Image($r('app.media.ic_reuse_02'))
.width(40)
.height(40)
.margin({ bottom: 10 }) // 向上凸起效果
}
.build() {
TabBar.barPosition(BarPosition.End)
TabContent() {
Text('首页内容')
.tabBar(this.myBuilder(0, '首页', $r('app.media.ic_tabbar_icon_1'), $r('app.media.ic_tabbar_1_selected')))
}
TabContent() {
Text('分类内容')
.tabBar(this.myBuilder(1, '分类', $r('app.media.ic_tabbar_icon_4'), $r('app.media.ic_tabbar_4_selected')))
}
// 🔥中间特殊按钮
TabContent() {
Text('活动内容')
.tabBar(this.centerBuilder())
}
TabContent() {
Text('购物车内容')
.tabBar(this.myBuilder(2, '购物车', $r('app.media.ic_tabbar_icon_2'), $r('app.media.ic_tabbar_2_selected')))
}
TabContent() {
Text('我的内容')
.tabBar(this.myBuilder(3, '我的', $r('app.media.ic_tabbar_icon_3'), $r('app.media.ic_tabbar_3_selected')))
.onChange((index: number) => {
this.selectedIndex = index
console.log('当前激活页:', index)
})
}
}
🔍细节说明:
• 中间按钮通过 .margin({ bottom: 10 }) 上移实现"凸起"效果。
• 选中态逻辑与普通页一致,只是视觉层面差异。
• 这种结构常见于商城、短视频、社交类App中。
🧠五、进阶拓展:TabBar 动效与图标联动
如果希望实现更流畅的动效,比如点击时放大图标或添加淡入淡出效果,可结合 animateTo() 或 TransitionEffect:
TypeScript
Image(this.selectedIndex === itemIndex ? selImg : img)
.width(30)
.height(30)
.transition(TransitionEffect.OPACITY) // 淡入淡出
.scale(this.selectedIndex === itemIndex ? 1.2 : 1.0)
.animation({ duration: 150 }) // 微动效
💡体验优化技巧:
• scale + animation:制造点击反馈感
• TransitionEffect:在状态切换时过渡更柔和
• 让TabBar不再"死板",而是具备品牌特征
🧱六、TypeScript 中的 class 类机制详解
在 ArkTS 中,class 是封装数据与逻辑的重要结构。


✅基础语法

TypeScript
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHello() {
console.log(`你好,我是 ${this.name},今年 ${this.age} 岁`)
}
}
const p = new Person('小鸿', 18)
p.sayHello()
✨constructor() 用于初始化对象属性,
this 关键字指向当前实例。
🌟继承机制
继承能让我们在组件逻辑上实现"模板 + 扩展"的效果。
TypeScript
class Animal {
constructor(public name: string) {}
speak() {
console.log(`${this.name} 在发声`)
}
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name)
}
speak() {
console.log(`${this.name}(${this.breed})在汪汪叫!`)
}
}
const myDog = new Dog('旺财', '柴犬')
myDog.speak()
✅ extends:继承父类
✅ super():调用父类构造函数或方法
✅ override:可以重写父类方法,灵活实现多态行为
一.类的字段与初始化
- 字段定义规则 :
- 字段名:类型 = 初始值
- 字段名?: 类型 (可选,可选字段访问时需用链式操作符避免报错)


小例:

例 1:Person 类
TypeScript
class Person {
name: string = 'jack'
food?: string
}
const p = new Person()
p.name = 'tom'
console.log(p.name)
console.log('字符串长度', p.food?.length) // 可选字段用链式操作符,找不到不报错
例 2:Cat 类
TypeScript
class Cat {
name: string = 'Tom'
food?: string
}
// 基于类创建对象
let p: Cat = new Cat()
console.log('姓名长度:', p.name.length)
console.log('食物长度:', p.food?.length)
p.food = '小黄鱼'
二.类的构造函数
当不同实例需要不同字段初始值时,通过构造函数实现初始化。

构造函数基础语法:
TypeScript
class 类名 {
字段A: 类型
字段B: 类型
constructor(参数...) {
// 通过new实例化时调用构造函数
// 通过this获取实例对象,给字段赋值
this.字段A = 参数
this.字段B = 参数
}
}
const 实例1 = new 类名(参数...)
const 实例2 = new 类名(参数...)
例:Food 类(直接传参)
TypeScript
class Food {
name: string
price: number
constructor(name: string, price: number) {
this.name = name
this.price = price
}
}
const f1 = new Food('西红柿鸡蛋', 15)
const f2 = new Food('土豆炖鸡块', 24) // 不同实例有不同初始值
例:Food 类(结合接口传参)
TypeScript
interface IFood {
name: string
price: number // 修正原笔记中price类型错误
desc: string
}
class Food {
constructor(paramsObj: IFood) {
this.name = paramsObj.name
this.price = paramsObj.price // 修正原笔记中赋值错误
this.desc = paramsObj.desc
}
}
let p1: Food = new Food({
name: '西兰花',
desc: '好吃',
price: 20
})
let p2: Food = new Food({
name: '黄瓜炒鸡蛋',
desc: '清爽',
price: 12
})
console.log('名称', p1.name)
console.log('名称', p2.name)
三.类的方法定义
类中可定义方法,用于封装逻辑,通过this
可访问实例对象的字段。
方法定义语法:
TypeScript
class 类名 {
方法名(参数...): 返回值类型 {
// 逻辑...
// 通过this获取实例对象
}
}
例:Person 类的 sayHi 方法
TypeScript
class Person {
name: string
constructor(name: string) {
this.name = name
}
sayHi(who: string) {
console.log(`你好${who},我是${this.name}`)
}
}
const p: Person = new Person('jack')
p.sayHi('rose')
❓七、常见问题 Q&A
Q1:为什么点击 Tab 图标没切换?
👉 确保 onChange() 正确绑定并更新 selectedIndex。
Q2:多个 Tab 状态不一致?
👉 确保每个 Tab 调用的 builder 传入的 itemIndex 唯一。
Q3:能否动态生成 Tabs?
👉 可以,循环数组生成 TabContent() 即可,非常适合后台配置导航的场景。
🏁结语
通过本篇内容,我们实现了从 默认 TabBar → 自定义样式 → 动效优化 → class 封装逻辑 的完整进阶。
这不仅是界面的美化,更是状态管理、组件封装与逻辑分层的实践。
未来你完全可以在此基础上扩展出:
• 动态加载图标资源
• 主题适配模式(亮/暗)
• 页面生命周期联动动画
💡掌握了 TabBar 的自定义技巧,也就掌握了 ArkUI 的一半交互核心!
恭喜您,迈出了鸿蒙应用开发坚实的一步!🎉
🤔 接下来,我将继续为大家更新鸿蒙开发的相关知识点,如果喜欢,记得关注驻波。
✅ 如果你觉得这篇 ArkUI 学习笔记对你有帮助,记得 点赞 + 收藏,关注我获取更多鸿蒙开发干货!📝 有任何问题欢迎在评论区留言,我们一起交流学习!
过往的博客欢迎各位大佬捧场。
从 0 到 1:用 ArkUI 写出你的第一个鸿蒙 App 界面 -CSDN博客
鸿蒙ArkUI布局与样式进阶(二)------ SVG 设计图案、Padding、Margin、Border、圆角与背景实战-CSDN博客
鸿蒙ArkUI布局与样式进阶(三)------线性布局(Row 与 Column)深度解析与工程实践-CSDN博客
鸿蒙ArkUI布局与样式进阶(四)------从 Column 到 Grid,一篇搞懂常用布局(含实战案例与逐行注释)_arkui gridrow按钮-CSDN博客
鸿蒙ArkUI布局与样式进阶(五)------从状态到界面:ArkUI 的响应式编程核心-CSDN博客
鸿蒙ArkUI布局与样式进阶(六)------ArkUI 支持的各种运算符和语法基础与数组操作-CSDN博客
鸿蒙ArkUI布局与样式进阶(七)------鸿蒙 ArkUI 开发中的条件判断与循环语句全解析(实战 + 思维延伸)-CSDN博客
对harmonyos感兴趣的培养记得关注驻波