關(guān)鍵詞:harmonyOS鴻蒙開(kāi)發(fā)??ArkTS??TabContent
使用場(chǎng)景:類微信底部導(dǎo)航欄,點(diǎn)擊/左右滑動(dòng)切換頁(yè)面并加載數(shù)據(jù)
開(kāi)發(fā)環(huán)境:ArkTS3.1? API9? Phone設(shè)備
HMOS Dev官方文檔:文檔中心
演示效果:

目錄
頁(yè)面切換時(shí)可加載新數(shù)據(jù)的完整代碼
完整Demo已提交至Gitee,傳送門:
鴻蒙ArkTS tabBar頁(yè)簽切換Demo: HarmonyOS鴻蒙ArkTS tabBar頁(yè)簽切換完整項(xiàng)目Demo
搭建頁(yè)面
我這里用三個(gè)頁(yè)面舉例,新建home、info、mine頁(yè)面,創(chuàng)建components目錄存放自定義Tabs組件,目錄結(jié)構(gòu)如下,我這里已經(jīng)準(zhǔn)備了6個(gè)圖標(biāo)圖片存放在了resources/rawfile/tabs
自定義Tabs(附完整代碼)
思路
index為應(yīng)用加載的首頁(yè),加載自定義Tabs組件,Tabs組件中加載各個(gè)頁(yè)面
開(kāi)始
HMOS Dev官方文檔 TabContent :文檔中心
1.首先在index.est中導(dǎo)入自定義Tabs組件,避免與官方組件沖突取名comTabs

2.在自定義Tabs組件中導(dǎo)入所建的三個(gè)頁(yè)面[如圖3],記得在頁(yè)面中使用export導(dǎo)出[如圖4]


[左圖3 | 右圖4]
3.設(shè)置一個(gè)PAGE的枚舉,增強(qiáng)可讀性,不喜歡用就0123代替,因?yàn)轫?yè)面切換的回調(diào)函數(shù)返回的數(shù)值從0開(kāi)始,所以給currentIndex默認(rèn)設(shè)置為0即為home頁(yè)(@State裝飾器修飾的屬性當(dāng)狀態(tài)改變時(shí),UI會(huì)發(fā)生對(duì)應(yīng)的渲染改變),changePage自己寫(xiě)來(lái)用于更新頁(yè)面數(shù)據(jù)函數(shù),稍后會(huì)用到。別忘了new TabsController()[如圖3]
4.官方提供了多種頁(yè)簽樣式,我們使用置于底部的,將Tabs中barPosition屬性設(shè)置為BarPosition.End。Tabs將占用整個(gè)頁(yè)面,所以寬高需設(shè)置為100%。
Tabs組件中需要TabContent來(lái)加載頁(yè)面。[如圖5]
在tabBar中自定義頁(yè)簽按鈕樣式,因重復(fù)代碼太多,我們可以利用@Builder裝飾器來(lái)自定義構(gòu)建函數(shù)復(fù)用代碼。[如圖6]
注意:自定義頁(yè)簽設(shè)置onClick事件用于改變頁(yè)面索引,Tabs組件需設(shè)置onChange事件,不然頁(yè)面左右滑動(dòng)頁(yè)簽狀態(tài)不會(huì)改變。

[圖5]

[圖6]??
完整自定義tabs代碼
// tabs.ets
// home頁(yè)import?Home?from?'../pages/home/home'// info頁(yè)import?Info?from?'../pages/info/info'// mine頁(yè)import?Mine?from?'../pages/mine/mine'
enum PAGE{
??HOME?= 0,
??INFO?= 1,
??MINE?= 2
}
@Preview
@Componentexport?default??struct compTabs{
??@State?currentIndex: number = 0;
??private tabsController: TabsController?= new?TabsController();
??changePage(e){
????this.currentIndex?= e
????if?(e == PAGE.HOME){
????}
????if?(e == PAGE.INFO){
????}
????if?(e == PAGE.MINE){
????}
??}
??@Builder?TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
????Column() {
??????Image(this.currentIndex?=== targetIndex ? selectedImg : normalImg)
????????.size({ width: 20, height: 20?}).objectFit(ImageFit.Fill)
??????Text(title)
????????.fontColor(this.currentIndex?=== targetIndex ? '#484D54'?: '#ff969da9')
????????.fontSize(10).margin({top:3,bottom:3})
????}
????.width('100%')
????.height(67)
????.alignItems(HorizontalAlign.Center)
????.backgroundColor('#fff')
????.justifyContent(FlexAlign.Center)
????.onClick(() =>?{
??????this.currentIndex?= targetIndex;
??????this.tabsController.changeIndex(targetIndex);
????})
??}
??build() {
????Tabs({ barPosition: BarPosition.End, controller: this.tabsController?}) {
??????TabContent() {
????????Home()
??????}.tabBar(this.TabBuilder('首頁(yè)', 0, $rawfile('tabs/home_a.png'), $rawfile('tabs/home.png')))
??????TabContent() {
????????Info()
??????}.tabBar(this.TabBuilder('信息', 1, $rawfile('tabs/service_a.png'), $rawfile('tabs/service.png')))
??????TabContent() {
????????Mine()
??????}.tabBar(this.TabBuilder('我的', 2, $rawfile('tabs/me_a.png'), $rawfile('tabs/me.png')))
????}.onChange((index: number) =>?{
??????this.changePage(index)
????})
????.width('100%')
????.height('100%')
??}
}
頁(yè)面切換后如何加載新數(shù)據(jù)
介紹
需要注意的是,在tabContent中不管有多少個(gè)頁(yè)面,頁(yè)面打開(kāi)后只會(huì)加載一次,切換頁(yè)面不會(huì)達(dá)到頁(yè)面更新的效果。那么頁(yè)面切換后,如何加載頁(yè)面的數(shù)據(jù)呢,很簡(jiǎn)單,可以利用官方提供的@Link裝飾器(父子雙向同步)向子組件傳遞一個(gè)時(shí)間戳參數(shù)過(guò)去,頁(yè)面中使用@Watch監(jiān)聽(tīng)這個(gè)時(shí)間戳屬性的變化觸發(fā)自定義的customShow函數(shù)。
或者可以增加if判斷頁(yè)面索引使其重新加載
父子組件解釋
在上述的tabs講述中導(dǎo)入了home頁(yè)面、info頁(yè)面、mine頁(yè)面,那這三個(gè)頁(yè)面就相當(dāng)于是tabs的子組件了
開(kāi)始
1.給子組件設(shè)置@Link修飾的timer屬性(@Link修飾不用賦初值)
2.利用@watch監(jiān)聽(tīng)一個(gè)自定義函數(shù)customShow,當(dāng)父組件的這個(gè)timer改變時(shí)子組件就會(huì)觸發(fā)這個(gè)函數(shù)[如圖9]
3.父組件Tabs設(shè)置三個(gè)屬性HomeTimer、InfoTimer、MineTimer,分別記錄每個(gè)頁(yè)面的時(shí)間戳,新增timeStamp函數(shù)返回當(dāng)前時(shí)間戳,在changePage函數(shù)觸發(fā)時(shí)獲取最新的時(shí)間戳[如圖7],TabContent中頁(yè)面里傳參時(shí)別忘了$符號(hào)[如圖8]。

[圖7]

[圖8]

[圖9]
同樣可以自定義頁(yè)面的銷毀等等,這是我在鴻蒙開(kāi)發(fā)中最常用最省事最高效的辦法了,大家不妨試試。
頁(yè)面切換時(shí)可加載新數(shù)據(jù)的完整代碼
Tabs.est
// Tabs.ets
// home頁(yè)import?Home?from?'../pages/home/home'// info頁(yè)import?Info?from?'../pages/info/info'// mine頁(yè)import?Mine?from?'../pages/mine/mine'
enum PAGE{
??HOME?= 0,
??INFO?= 1,
??MINE?= 2
}
@Preview
@Componentexport?default??struct compTabs{
??@State?currentIndex: number = 0;
??@State?HomeTimer: number = 0;
??@State?InfoTimer: number = 0;
??@State?MineTimer: number = 0;
??private tabsController: TabsController?= new?TabsController();
??changePage(e){
????this.currentIndex?= e
????if?(e == PAGE.HOME){
??????this.HomeTimer?= this.timeStamp()
????}
????if?(e == PAGE.INFO){
??????this.InfoTimer?= this.timeStamp()
????}
????if?(e == PAGE.MINE){
??????this.MineTimer?= this.timeStamp()
????}
??}
??timeStamp(){
????return?new?Date().getTime();
??}
??@Builder?TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
????Column() {
??????Image(this.currentIndex?=== targetIndex ? selectedImg : normalImg)
????????.size({ width: 20, height: 20?}).objectFit(ImageFit.Fill)
??????Text(title)
????????.fontColor(this.currentIndex?=== targetIndex ? '#484D54'?: '#ff969da9')
????????.fontSize(10).margin({top:3,bottom:3})
????}
????.width('100%')
????.height(67)
????.alignItems(HorizontalAlign.Center)
????.backgroundColor('#fff')
????.justifyContent(FlexAlign.Center)
????.onClick(() =>?{
??????this.currentIndex?= targetIndex;
??????this.tabsController.changeIndex(targetIndex);
????})
??}
??build() {
????Tabs({ barPosition: BarPosition.End, controller: this.tabsController?}) {
??????TabContent() {
????????Home({ timer:$HomeTimer })
??????}.tabBar(this.TabBuilder('首頁(yè)', 0, $rawfile('tabs/home_a.png'), $rawfile('tabs/home.png')))
??????TabContent() {
????????Info({ timer:$InfoTimer })
??????}.tabBar(this.TabBuilder('信息', 1, $rawfile('tabs/service_a.png'), $rawfile('tabs/service.png')))
??????TabContent() {
????????Mine({ timer:$MineTimer })
??????}.tabBar(this.TabBuilder('我的', 2, $rawfile('tabs/me_a.png'), $rawfile('tabs/me.png')))
????}.onChange((index: number) =>?{
??????this.changePage(index)
????})
????.width('100%')
????.height('100%')
??}
}
home.est
import?promptAction from?'@ohos.promptAction';
@Componentexport?default?struct Home?{
??@State?message: string = 'home頁(yè)面'
??@Link?@Watch('customShow') timer: Number
??customShow(){
????promptAction.showToast({
??????message: "頁(yè)面展示"
????});
??}
??build() {
????Row() {
??????Column() {
????????Text(this.message)
??????????.fontSize(50)
??????????.fontWeight(FontWeight.Bold)
??????}
??????.width('100%')
????}
????.height('100%')
??}
}