【HarmonyOS NEXT】Tabs組件實(shí)現(xiàn)類微信(可滑動(dòng)的)tabBar頁(yè)簽切換頁(yè)面功能

關(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官方文檔:文檔中心

演示效果:

目錄

完整Demo已提交至Gitee

搭建頁(yè)面

自定義TabContent(往后翻有完整代碼)

思路

開(kāi)始

完整自定義tabs代碼

頁(yè)面切換后如何加載新數(shù)據(jù)

介紹

父子組件解釋

開(kāi)始

頁(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%')

??}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容