问题复现
一直以来,首页的Tabs是这么用的:
TypeScript
import Home from "../pages/home/Home"
import ZhiHu from "../pages/song/Song"
import Mine from "../pages/mine/Mine"
@Entry
@Component
struct Index {
@State currentIndex: number = 0
private controller: TabsController = new TabsController()
// 只有被@Entry装饰的组件才可以调用页面的生命周期
onPageShow() {
console.info('Index onPageShow');
}
// 只有被@Entry装饰的组件才可以调用页面的生命周期
onPageHide() {
console.info('Index onPageHide');
}
// 只有被@Entry装饰的组件才可以调用页面的生命周期
onBackPress() {
console.info('Index onBackPress');
}
build() {
Column() {
Tabs({
barPosition: BarPosition.End,
controller: this.controller
}) {
TabContent() {
Home()
}.tabBar(this.TabBuilder('首页', 0, $r('app.media.icon_sort'), $r('app.media.icon_sort_default')))
TabContent() {
ZhiHu()
}.tabBar(this.TabBuilder('原声', 1, $r('app.media.ks_selected'), $r('app.media.ks')))
TabContent() {
Mine()
}.tabBar(this.TabBuilder('我的', 2, $r('app.media.user_selected'), $r('app.media.user')))
}.scrollable(false) // 禁止滑动切换
}
.width('100%')
.height('100%')
}
// 自定义导航页签的样式
@Builder TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
Column() {
Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
.size({ width: 25, height: 25 })
Text(title)
.fontColor(this.currentIndex === targetIndex ? '#28bff1' : '#8a8a8a')
}
.width('100%')
.height(50)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.currentIndex = targetIndex
this.controller.changeIndex(this.currentIndex)
})
}
}
之前一直没留意到,跳转到 子页面后,这底部的几个tab竟然还在,没有隐藏掉。这是什么情况?其实是用法问题。
在元服务的也存在这个问题,之前Index页是这么写的:
TypeScript
import Home from "./home/Home";
import Mine from "./mine/Mine";
import { AtomicServiceTabs, OnContentWillChangeCallback, TabBarOptions, TabBarPosition } from '@kit.ArkUI';
import { Log } from "../utils/logutil";
import { authentication } from "@kit.AccountKit";
import { BusinessError } from "@kit.BasicServicesKit";
@Entry
@Component
struct Index {
// 组件生命周期
aboutToAppear() {
Log.info('Index aboutToAppear');
this.loginWithHuaweiID();
}
// 组件生命周期
aboutToDisappear() {
Log.info('Index aboutToDisappear');
}
@State currentIndex: number = 0
onContentWillChangeCallBack: OnContentWillChangeCallback = (currentIndex: number, comingIndex: number): boolean => {
this.currentIndex = currentIndex;
console.log('OnContentWillChangeCallback')
return true;
}
onTabClick: Callback<number> = (index: number) => {
console.log('onTabClick');
}
@Builder
Mine() {
Column() {
Mine()
}.width('100%').height('100%')
}
@Builder
Home() {
Column() {
Home()
}.width('100%').height('100%')
}
build() {
Stack() {
AtomicServiceTabs({
tabContents: [
() => {
this.Home()
},
() => {
this.Mine()
},
],
tabBarOptionsArray: [
new TabBarOptions($r('sys.media.leave_home_fill'), '首页', Color.Black, Color.Blue),
new TabBarOptions($r('sys.media.AI_pause'), '我的', Color.Black, Color.Blue),
],
tabBarPosition: TabBarPosition.BOTTOM,
barBackgroundColor: $r('sys.color.ohos_id_color_bottom_tab_bg'),
onTabBarClick: this.onTabClick,
onContentWillChange: this.onContentWillChangeCallBack,
})
}.height('100%')
}
}
}
解决办法
在Index页面中,用Navigation把tabs包裹起来。然后,注意在tab的页面中,不要再用Navigation包裹了,只在这Index页这里保留一份。那么tab页的title呢?自己用个Text自定义实现就行啦。
那么tab页中,如何跳转到子页面呢?
然后重点来了,用@Provide装饰器和@Consume装饰器。
@Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,@Provide和@Consume摆脱参数传递机制的束缚,实现跨层级传递。
其中@Provide装饰的变量是在祖先组件中,可以理解为被"提供"给后代的状态变量。@Consume装饰的变量是在后代组件中,去"消费(绑定)"祖先组件提供的变量。
@Provide/@Consume是跨组件层级的双向同步。
在Index页面用以下方式定义个pageStack。
TypeScript
@Entry
@Component
struct Index {
@State currentIndex: number = 0
private controller: TabsController = new TabsController()
//定义pageStack
@Provide pageStack: NavPathStack = new NavPathStack()
//......
}
在tab的页面中,通过使用@Consume去获取@Provide提供的变量。
TypeScript
@Component
export default struct Home {
//Consume,去"消费(绑定)"祖先组件提供的变量
@Consume pageStack: NavPathStack
private swiperController: SwiperController = new SwiperController()
private swiperData: SwiperDataSource<BannerItem> = new SwiperDataSource()
//......
this.pageStack.pushDestinationByName("MusicPlayerPage", { item:it }).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
}