場景介紹
部分應(yīng)用在使用過程中需要自定義添加或刪除標(biāo)簽的場景,比如在瀏覽器中的頂部標(biāo)簽欄中需要新打開或關(guān)閉網(wǎng)頁頁簽,而這種場景與Tabs組件效果類似,但Tabs組件不提供增加或刪除頁簽的功能,不能自由的增加刪除頁簽,需要開發(fā)者自己實(shí)現(xiàn)Tabs中增刪頁簽功能。本文以瀏覽器中增加或刪除頁簽為例,實(shí)現(xiàn)Tabs中頁簽的增刪功能。
效果呈現(xiàn)
如下動(dòng)圖所示:
環(huán)境要求
本例基于以下環(huán)境開發(fā),開發(fā)者也可以基于其他適配的版本進(jìn)行開發(fā):
實(shí)現(xiàn)原理
本例涉及的關(guān)鍵特性以及實(shí)現(xiàn)方案如下:
- 通過@Builder自定義封裝一個(gè)導(dǎo)航頁簽欄,并通過ForEach完成對Tabs組件的內(nèi)容頁和導(dǎo)航頁簽欄的動(dòng)態(tài)渲染。
- 通過TabsController的changeIndex可實(shí)現(xiàn)頁面的跳轉(zhuǎn),傳入的index是數(shù)組中對應(yīng)的索引值。
- 頁簽的增加通過數(shù)組的push方法,增加數(shù)組元素。
- 刪除頁簽通過通過刪除頁面對應(yīng)數(shù)組的索引值處的數(shù)據(jù)完成,刪除后頁面跳轉(zhuǎn)位置根據(jù)業(yè)務(wù)邏輯要求確定跳轉(zhuǎn)頁面對應(yīng)的索引值。
開發(fā)步驟
整體布局分為兩部分:頁面內(nèi)容和頁簽部分。頁面視圖部分使用Tabs,其中頁簽對應(yīng)顯示的內(nèi)容需要放入TabContent組件中。頁簽部分通過@Builder自定義封裝一個(gè)tabBar組件,放入Tabs中的tabBar屬性中。
頁面視圖代碼塊:Tabs({ barPosition: BarPosition.Start, controller: this.controller }) { ForEach(this.tabArray, (item: number) = > { TabContent() { Text('我是頁面 ' + item + " 的內(nèi)容") .height('100%') .width('100%') .fontSize(30) .backgroundColor("#ffffffff") }.tabBar(this.Tab(item)) }, (item: number) = > item.toString() + util.generateRandomUUID()) } .barMode(BarMode.Scrollable, { margin: 30 }) .onChange((index) = > { this.focusIndex = index }) .barHeight(30)
自定義tabBar代碼塊:
//控制頁簽渲染的數(shù)組 @State tabArray: Array< number > = [0] //Tabs組件當(dāng)前顯示的頁簽 @State focusIndex: number = 0 //創(chuàng)建頁簽時(shí)的頁簽index @State pre: number = -1 //Tabs組件控制器,根據(jù)組件下標(biāo)控制tab跳轉(zhuǎn) private controller: TabsController = new TabsController() //自定義導(dǎo)航頁簽欄 @Builder Tab(tabNumber: number) { Row({ space: 20 }) { Text("頁簽 " + tabNumber).fontSize(18) Image($r('app.media.ic_public_cancel_filled')).width(20).height(20) } .justifyContent(FlexAlign.Center) .constraintSize({ minWidth: 35 }) .width(120) .height(30) .borderRadius({ topLeft: 10, topRight: 10 }) .backgroundColor(this.tabArray.indexOf(tabNumber) === this.focusIndex ? "#ffffffff" : "#ffb7b7b7") }
實(shí)現(xiàn)頁簽和頁面視圖的聯(lián)動(dòng):這里主要通過TabsController的changeIndex來實(shí)現(xiàn)對應(yīng)的試圖跳轉(zhuǎn),但需要注意由于之后會增刪數(shù)組元素的操作,所以此處傳入的index值是選擇頁面的tabNumber在數(shù)組中的索引值。
this.focusIndex = this.tabArray.indexOf(tabNumber)
增添數(shù)組元素實(shí)現(xiàn)增加頁簽的效果:增添數(shù)組元素使用數(shù)組的push方法在tabArray添加數(shù)據(jù)即可,但由于此demo原始定義的數(shù)組是連續(xù)的自然數(shù),后續(xù)增刪數(shù)組會打亂原有順序,所以此處處理為先判斷最后一個(gè)元素的值再加1,由于TabBar的渲染是通過ForEach被@State修飾的數(shù)組,因此當(dāng)tabArray中添加數(shù)據(jù)后系統(tǒng)會通知ForEach便利數(shù)組重新渲染頁面。
this.tabArray.push(this.tabArray[this.tabArray.length - 1] + 1)
通常在添加新頁面后,瀏覽器會將頁面跳轉(zhuǎn)到新添加的頁面,因此在向tabArray中完成數(shù)據(jù)推送后,需要將頁簽通過TabsController中的changeIndex方法跳轉(zhuǎn)到最后一個(gè)。
this.focusIndex = this.tabArray.length - 1 this.controller.changeIndex(this.focusIndex)
當(dāng)用戶選擇另一個(gè)頁簽時(shí),可通過自定義頁簽中通用事件onClick進(jìn)行控制,當(dāng)用戶點(diǎn)擊待選擇的頁簽后,獲取當(dāng)前頁簽對應(yīng)的下標(biāo),然后通過TabsController中的changeIndex方法進(jìn)行跳轉(zhuǎn),此外可以通過更改頁簽背景顏色標(biāo)識被選中頁簽的。
.backgroundColor(this.tabArray.indexOf(tabNumber) === this.focusIndex ? "#ffffffff" : "#ffb7b7b7") .onClick(() = > { this.focusIndex = this.tabArray.indexOf(tabNumber) this.controller.changeIndex(this.focusIndex) })
刪除頁面有三種情況(刪除不同位置的頁面)。
第一種情況是被關(guān)閉頁面為最后一個(gè)頁面,且當(dāng)前選中的頁面為最后一個(gè)頁面,如果當(dāng)前被選中頁面是剛剛被其他頁面創(chuàng)建的情況,因此頁面需要跳回到創(chuàng)建被刪除頁簽的頁簽(邏輯參考chrome瀏覽器)
if (this.pre >= 0) { this.focusIndex = this.pre } this.tabArray.splice(this.tabArray.indexOf(tabNumber), 1)
如果不是的話直接將當(dāng)前顯示頁簽下下標(biāo)設(shè)置為前一個(gè)頁簽下標(biāo),tabArray數(shù)組通過splice方法刪除頁簽,并通過TabsController完成跳轉(zhuǎn),此外頁面只要有關(guān)閉操作,頁面就不可以跳回打開該頁面的頁面,即將 pre設(shè)置為-1。
this.focusIndex = this.focusIndex - 1 this.tabArray.splice(this.tabArray.indexOf(tabNumber), 1) this.pre = -1 this.controller.changeIndex(this.focusIndex)
第二種情況,當(dāng)用戶當(dāng)前選擇的不是最后一個(gè)標(biāo)簽,然后直接刪除其他頁簽時(shí),可以直接刪除刪除,但是需要重新計(jì)算當(dāng)前選中頁簽在tabArray中的實(shí)時(shí)位置,到新注意需要排除用戶在最后一個(gè)頁簽刪除當(dāng)前頁簽的情況。
//當(dāng)前選擇頁面的對應(yīng)數(shù)組中的數(shù)據(jù)值 let focusNumber = this.tabArray[this.focusIndex] //用于判斷是否是用戶在最后一個(gè)頁簽刪除當(dāng)前頁簽的情況 if (this.tabArray.indexOf(focusNumber) >= 0) { this.focusIndex = this.tabArray.indexOf(focusNumber) } this.controller.changeIndex(this.focusIndex)
第三種情況,當(dāng)用戶當(dāng)前選擇的不是最后一個(gè)標(biāo)簽,且刪除被選中頁面,直接刪除,然后通過TabsController完成跳轉(zhuǎn),不需要額外操作。
說明
- 由于Tabs組件中的導(dǎo)航頁簽欄會占滿屏幕,導(dǎo)致添加按鈕無法直接添加到與頁簽直接平齊的位置,因此通過層疊布局(Stack)的方式,將添加頁簽按鈕覆蓋到Tabs組件上,通過Stack中的對齊方式將添加按鈕調(diào)整到合適位置。
- 用于Tabs添加或刪除子節(jié)點(diǎn)時(shí),F(xiàn)orEach需要重新將所有頁簽進(jìn)行重新渲染,如果在添加或刪除完頁簽后直接調(diào)用TabsController中的changeIndex進(jìn)行跳轉(zhuǎn),頁面無法調(diào)到指定頁簽。這是由于ForEach還未將組件渲染完成,將子組件掛載到Tabs中,因此建議通過setTimeOut延遲一段時(shí)間再進(jìn)行跳轉(zhuǎn),經(jīng)過驗(yàn)證大概50ms后即可,開發(fā)者可再自行驗(yàn)證。
setTimeout(() = > { this.controller.changeIndex(this.focusIndex) }, 50)
完整實(shí)例
完整示例代碼如下:
import util from '@ohos.util'
@Entry
@Component
struct Drag {
//控制頁簽渲染的數(shù)組
@State tabArray: Array< number > = [0]
//Tabs組件當(dāng)前顯示的頁簽下標(biāo)
@State focusIndex: number = 0
//創(chuàng)建頁簽時(shí)的頁簽index
@State pre: number = -1
//Tabs組件控制器,根據(jù)組件下標(biāo)控制tab跳轉(zhuǎn)
private controller: TabsController = new TabsController()
// 單獨(dú)的頁簽
@Builder
Tab(tabNumber: number) {
Row({ space: 20 }) {
Text("頁簽 " + tabNumber).fontSize(18)
Image($r('app.media.ic_public_cancel_filled')).width(20).height(20).onClick(() = > {
//獲取Tabs組件當(dāng)前顯示的頁簽中顯示的數(shù)字
let focusNumber = this.tabArray[this.focusIndex]
//被刪除的頁簽是否是當(dāng)前選中的頁簽,且是最后一個(gè)頁簽
if (this.focusIndex === this.tabArray.indexOf(tabNumber) && this.focusIndex == this.tabArray.length - 1) {
//判斷是否需要跳回到創(chuàng)建該頁簽時(shí)的頁簽,如果不需要直接跳轉(zhuǎn)到前一個(gè)頁簽
if (this.pre >= 0) {
this.focusIndex = this.pre
} else {
this.focusIndex = this.focusIndex - 1
}
}
this.tabArray.splice(this.tabArray.indexOf(tabNumber), 1)
this.pre = -1
//對應(yīng)刪除頁面中的第二種情況
if (this.tabArray.indexOf(focusNumber) >= 0) {
this.focusIndex = this.tabArray.indexOf(focusNumber)
}
//設(shè)置50ms 延遲跳轉(zhuǎn)
setTimeout(() = > {
this.controller.changeIndex(this.focusIndex)
}, 50)
})
}
.justifyContent(FlexAlign.Center)
.constraintSize({ minWidth: 35 })
.width(120)
.height(30)
.borderRadius({ topLeft: 10, topRight: 10 })
.backgroundColor(this.tabArray.indexOf(tabNumber) === this.focusIndex ? "#ffffffff" : "#ffb7b7b7")
.onClick(() = > {
this.focusIndex = this.tabArray.indexOf(tabNumber)
setTimeout(() = > {
this.controller.changeIndex(this.focusIndex)
}, 50)
})
}
build() {
Column() {
Column() {
// 頁簽
// Row() {
Stack() {
Row({ space: 7 }) {
//tabs
Tabs({ barPosition: BarPosition.Start, controller: this.controller }) {
ForEach(this.tabArray, (item: number) = > {
TabContent() {
Text('我是頁面 ' + item + " 的內(nèi)容")
.height('100%')
.width('100%')
.fontSize(30)
.backgroundColor("#ffffffff")
}.tabBar(this.Tab(item))
}, (item: number) = > item.toString() + util.generateRandomUUID())
}
.barMode(BarMode.Scrollable, { margin: 30 })
.onChange((index) = > {
this.focusIndex = index
})
.barHeight(30)
}.width("100%")
Row() {
Image($r('app.media.ic_public_add_filled')).onClick(() = > {
if (this.tabArray.length === 0) {
this.tabArray.push(0)
this.focusIndex = this.tabArray.length - 1
} else {
this.pre = this.focusIndex
this.tabArray.push(this.tabArray[this.tabArray.length - 1] + 1)
this.focusIndex = this.tabArray.length - 1
}
setTimeout(() = > {
this.controller.changeIndex(this.focusIndex)
}, 50)
}).width(20).height(20)
}.height(30).width(30).backgroundColor("#ffb7b7b7")
.justifyContent(FlexAlign.Center)
}
.alignContent(Alignment.TopEnd)
.width('100%')
.backgroundColor("#ffb7b7b7")
}
.alignItems(HorizontalAlign.Start)
.width('100%')
}
.height('100%')
}
}
本文主要講解在鴻蒙開發(fā)當(dāng)中ArkUI中的增刪Tab頁簽實(shí)現(xiàn),更多的鴻蒙ArkUI學(xué)習(xí),可以參考下面的學(xué)習(xí)曲線圖:
完整版鴻蒙os與openharmony技術(shù)路線與文檔,可在主頁保存
-
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2638瀏覽量
66768 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2397瀏覽量
43098 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3753瀏覽量
16670
發(fā)布評論請先 登錄
相關(guān)推薦
鴻蒙ArkUI開發(fā)-Video組件的使用
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>ArkUI</b><b class='flag-5'>開發(fā)</b>-Video組件的使用](https://file1.elecfans.com/web2/M00/BD/F9/wKgZomWvfTKAL6hGAAFx1rpKJH4601.jpg)
鴻蒙開發(fā)ArkUI-X基礎(chǔ)知識:【ArkUI跨平臺設(shè)計(jì)總體說明】
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>ArkUI</b>-X基礎(chǔ)知識:【<b class='flag-5'>ArkUI</b>跨平臺設(shè)計(jì)總體說明】](https://file1.elecfans.com/web2/M00/E5/FC/wKgZomZFvuqAaa9zAACamI3wixM971.jpg)
鴻蒙開發(fā)ArkUI-X基礎(chǔ)知識:【ArkUI代碼工程及構(gòu)建介紹】
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>ArkUI</b>-X基礎(chǔ)知識:【<b class='flag-5'>ArkUI</b>代碼工程及構(gòu)建介紹】](https://file1.elecfans.com/web2/M00/C5/CD/wKgZomYCdwyAIFf5AAB_7E1pFms943.jpg)
鴻蒙開發(fā)-ArkUI框架實(shí)戰(zhàn)【日歷應(yīng)用 】
OpenHarmony JS UI小型系統(tǒng)實(shí)現(xiàn)tab頁簽容器的方法
鴻蒙閃屏頁是怎么實(shí)現(xiàn)?
HDD杭州站?ArkUI讓開發(fā)更靈活
編程小白也能快速掌握的ArkUI JS組件開發(fā)
ArkUI新能力,助力應(yīng)用開發(fā)更便捷
鴻蒙ArkUI-X跨平臺技術(shù):【開發(fā)準(zhǔn)備】
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>ArkUI</b>-X跨平臺技術(shù):【<b class='flag-5'>開發(fā)</b>準(zhǔn)備】](https://file1.elecfans.com/web2/M00/E6/FF/wKgaomZF_t6AQdF0AAOgQ8sKRgk267.jpg)
鴻蒙ArkUI-X跨平臺開發(fā):【 編寫第一個(gè)ArkUI-X應(yīng)用】
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>ArkUI</b>-X跨平臺<b class='flag-5'>開發(fā)</b>:【 編寫第一個(gè)<b class='flag-5'>ArkUI</b>-X應(yīng)用】](https://file1.elecfans.com/web2/M00/E7/6A/wKgaomZHYpCAfHdmAABRB5pcYsA368.jpg)
評論