一:起因
因为今晚写代码的时候发现由于目前Tabs组件不支持设置上面页签的位置和方式,但是我又想改变一下那个样式,然后就有了一个想法自定义一个tabber去盖住原来的
二:思路
-
状态管理:使用
@State声明选项卡数据和当前选中索引。 -
选项卡控制器:使用
TabsController控制选项卡的切换。 -
自定义选项卡栏:通过
@Builder方法getTabBar动态生成选项卡栏。 -
布局构建:
- 使用
Stack布局将选项卡组件和自定义选项卡栏堆叠在一起。 - 使用
Tabs组件显示选项卡内容。 - 使用
Row组件显示自定义选项卡栏。
- 使用
三:一个重点的原理
在鸿蒙开发中,@Builder 方法用于动态生成组件,这意味着这些组件是在运行时根据当前状态和数据创建的。通过 @Builder 方法生成的组件能够根据变化的数据进行实时更新,而不需要重新构建整个组件树。
下面是机制:
-
动态生成的定义: 动态生成意味着组件在运行时根据当前的状态和数据动态创建和更新,而不是在编译时就固定不变。这样可以使 UI 更加灵活和响应变化。
-
@Builder方法的作用:@Builder方法是鸿蒙框架中的一个装饰器,用于声明一个方法为组件生成器。当组件需要更新时,框架会自动调用这些生成器方法来重新构建需要更新的部分,而不是重新构建整个页面。 -
实时响应状态变化: 当
@Builder方法内使用到的状态变量(比如this.currentIndex)发生变化时,框架会触发这个方法重新执行,从而生成新的组件树。这使得选项卡栏能够根据选中的索引实时更新其样式和内容。
四:核心api
-
@State:声明组件的状态变量。 -
Tabs:创建选项卡组件。 -
TabContent:定义选项卡的内容。 -
tabBar:设置选项卡的标签。 -
ForEach:遍历数组创建组件。 -
Column和Row:垂直和水平布局组件。 -
Text和Divider:文本和分隔线组件。 -
onClick:设置点击事件处理函数。 -
Stack:堆叠布局组件。
五:代码
1自定义选项卡栏的生成方法
getTabBar(item: TabClass) {
Column() {
Text(item.title)
.fontSize(16)
.fontColor(this.tabsData[this.currentIndex].name === item.name ? $r('app.color.text_primary') : $r('app.color.text_secondary'))
.fontWeight(600)
.animation({
duration: 300
})
.margin({
bottom: 10
})
Divider()
.strokeWidth(4)
.color($r('app.color.primary'))
.lineCap(LineCapStyle.Round)
.width(this.tabsData[this.currentIndex].name === item.name ? 23 : 0)
.animation({
duration: 300
})
}
.onClick(async () => {
const index = this.tabsData.findIndex((i => i.name === item.name))
this.tabController.changeIndex(index)
})
}
.onClick(async () => {...}):设置点击事件,当点击选项卡栏时切换选项卡。
const index = this.tabsData.findIndex((i => i.name === item.name)):找到点击的选项卡的索引。this.tabController.changeIndex(index):切换到点击的选项卡
2主构建方法
Stack({ alignContent: Alignment.Top }) {
Tabs({ barPosition: BarPosition.Start, index: $$this.currentIndex, controller: this.tabController }) {
ForEach(this.tabsData, (item: TabClass) => {
TabContent() {
Text(item.title)
}.tabBar(item.title)
})
}.backgroundColor($r('app.color.background_page')).animationDuration(300)
Row({ space: 30 }) {
ForEach(this.tabsData, (item: TabClass) => {
this.getTabBar(item)
})
}
.padding({
left: 40,
right: 40
})
.width('100%')
.height(50)
.backgroundColor($r("app.color.white"))
}
}
-
Stack({ alignContent: Alignment.Top }):使用堆叠布局,将Tabs和自定义选项卡栏垂直排列在一起。 -
Tabs({ barPosition: BarPosition.Start, index: $$this.currentIndex, controller: this.tabController }):创建选项卡组件。-
barPosition: BarPosition.Start:设置选项卡栏位置在顶部。 -
index: $$this.currentIndex:绑定当前选中的选项卡索引。 -
controller: this.tabController:绑定选项卡控制器。
-
-
ForEach(this.tabsData, (item: TabClass) => {...}):遍历选项卡数据,为每个选项卡创建内容和标签。 -
ForEach(this.tabsData, (item: TabClass) => {...}):遍历选项卡数据,为每个选项卡创建自定义选项卡栏。 -
this.getTabBar(item):调用getTabBar方法生成自定义选项卡栏。
六:成果
这个是原来自带的
这个是我们自定义了之后,覆盖掉了原来自带的