本文是工作中用到的笔记📒,记录使用 Tabs 组件实现导航条功能。
目录:
导航条默认出现在页面顶部。
@Entry
@Component
struct TestTabs {
build() {
Tabs() {
TabContent() { Text('首页内容') }.tabBar('首页')
TabContent() { Text('我的内容') }.tabBar('我的')
}
}
}
微信的导航条默认就在底部显示。
@Entry
@Component
struct TestTabs {
build() {
Tabs({ barPosition: BarPosition.End }) {
TabContent() {
Text('首页内容')
}.tabBar('首页')
TabContent() {
Text('我的内容')
}.tabBar('我的')
}
}
}
外卖平台的点单页面导航条默认在左侧显示。
@Entry
@Component
struct TestTabs {
build() {
// 当 vertical 为 true 时,Start 在左边,End 在右边
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
Text('首页内容')
}.tabBar('首页')
TabContent() {
Text('我的内容')
}.tabBar('我的')
}
// 垂直显示
.vertical(true)
// 手动固定导航条宽度
.barWidth(100)
}
}
下面示例代码中的图标地址需要自行替换。
@Entry
@Component
struct TestTabs {
// 控制导航栏切换
@State currentIndex: number = 0;
// 控制导航对应的视图切换
private tabsController : TabsController = new TabsController();
// 自定义导航内容
@Builder
TabBuilder(title: string, index: number, img: Resource, activeImg: Resource) {
Column() {
Image(this.currentIndex === index ? activeImg : img)
.width(25)
.height(25)
Text(title).fontColor(this.currentIndex === index ? '#1698CE' : '#6B6B6B')
}
.onClick(() => {
this.currentIndex = index;
this.tabsController.changeIndex(index)
})
}
build() {
Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {
TabContent() {
Text('首页内容')
}.tabBar(this.TabBuilder('首页', 0, $r('app.media.ic_home_normal'), $r('app.media.ic_home_selected')))
TabContent() {
Text('我的内容')
}.tabBar(this.TabBuilder('我的', 1, $r('app.media.ic_mine_normal'), $r('app.media.ic_mine_selected')))
}
}
}
导航对应的页面比较复杂时,会将导航内容写成一个独立的组件,如下代码,Home() 和 Mine() 分别是两个导航对应的页面。
@Entry
@Component
struct TestTabs {
build() {
Tabs({ barPosition: BarPosition.End }) {
TabContent() {
Home()
}.tabBar('首页')
TabContent() {
Mine()
}.tabBar('我的')
}
}
}
@Component
struct Home {
aboutToAppear(): void {
console.log('Home component load ...')
// 每次切换到首页视图,都想要查询数据
}
build() {
Text('首页内容')
}
}
@Component
struct Mine {
build() {
Text('我的内容')
}
}
此时有一个问题:每次进入导航条对应的页面,需要做一些初始化数据的操作,最开始想到的是 aboutToAppear(),含义是组件第一次加载时触发,测试时发现来回切换导航条,aboutToAppear() 只会触发一次。
原因:
🐟 比如当前位于A导航条,A导航条对应的页面第一次加载时会触发 aboutToAppear();
🐟 现在从 A 导航条切换到 B 导航条,A 导航对应的页面隐藏起来,并不是直接销毁;
🐟 从 B 导航条再切换回 A 导航条时,由于 A 导航条对应的页面之前已经存在了,此时是不会触发 A 导航条对应页面的 aboutToAppear() 函数的。
这里介绍两个解决方法:
☁️ 导航条数目很少时,使用 TabContent().onWillShow() 监听切换触发事件;
☁️ 导航条数目很多时,使用 Tabs().onChange() 监听切换触发事件。
@Entry
@Component
struct TestTabs {
build() {
Tabs({ barPosition: BarPosition.End }) {
TabContent() {
Home()
}
.tabBar('首页')
.onWillShow(() => {
console.log('每次切换到首页导航条时都会触发...')
})
TabContent() {
Mine()
}.tabBar('我的')
.onWillShow(() => {
console.log('每次切换到我的导航条时都会触发...')
})
}
.onChange((index: number) => {
console.log('当前切换的导航条下标', index)
})
}
}
@Component
struct Home {
build() {
Text('首页内容')
}
}
@Component
struct Mine {
build() {
Text('我的内容')
}
}
现在可以监控导航条切换,但是最终目的是想要改变 Home 内部的变量值,可以使用 @StorageLink
装饰器,在 TestTabs 和 Home 中都添加变量定义,不管哪一边修改变量值,另一边的变量值也会同步改变。
完整示例代码如下:
@Entry
@Component
struct TestTabs {
@StorageLink('productList') productList: string[] = [];
build() {
Tabs({ barPosition: BarPosition.End }) {
TabContent() {
Home()
}
.tabBar('首页')
.onWillShow(() => {
console.log('每次切换到首页导航条时都会触发...')
// 模拟异步请求
setTimeout(() => {
// 修改 TestTabs 中的 productList 变量值
// Home 中的 productList 值会自动更新
this.productList = [ 'a', 'b', 'c' ];
}, 3000)
})
TabContent() {
Mine()
}.tabBar('我的')
.onWillShow(() => {
console.log('每次切换到我的导航条时都会触发...')
})
}
.onChange((index: number) => {
console.log('当前切换的导航条下标', index)
})
}
}
@Component
struct Home {
@StorageLink('productList') productList: string[] = [];
build() {
Column() {
Text('首页内容')
ForEach(this.productList, (item: string) => {
Text(`Product ${item}`)
})
}
}
}
@Component
struct Mine {
build() {
Text('我的内容')
}
}
↶ 返回首页 ↶