系列文章目录
HarmonyOS Next 系列之省市区弹窗选择器实现(一)
HarmonyOS Next 系列之验证码输入组件实现(二)
HarmonyOS Next 系列之底部标签栏TabBar实现(三)
HarmonyOS Next 系列之HTTP请求封装和Token持久化存储(四)
HarmonyOS Next 系列之从手机选择图片或拍照上传功能实现(五)
HarmonyOS Next 系列之可移动悬浮按钮实现(六)
HarmonyOS Next 系列之沉浸式状态实现的多种方式(七)
HarmonyOS Next系列之Echarts图表组件(折线图、柱状图、饼图等)实现(八)
HarmonyOS Next系列之地图组件(Map Kit)使用(九)
HarmonyOS Next系列之半圆环进度条实现(十)
HarmonyOS Next 系列之列表下拉刷新和触底加载更多数据实现(十一)
HarmonyOS Next系列之实现一个左右露出中间大两边小带缩放动画的轮播图(十二)
前言
HarmonyOS Next(基于API11)实现页面级容器——底部标签栏TabBar
一、实现原理
Tabs+TabContent+自定义tabbar构建整个页面级TabBar容器,其中子组件TabContent的tabBar属性支持传入自定义构建函数,通过自定义构建函数可以灵活布局整个底部标签样式,最后通过TabsController控制器实现页面切换
二、代码实现
1.自定义构建函数绘制单个标签样式
//tabBuilder自定义构建函数入参类型
interface BuilderParams {
index: number //标签索引
label: string //标签名称
normalIcon: Resource //未选中状态图标
selectIcon: Resource //选中状态图标
}
@Entry
@Component
struct TabBar{
controller: TabsController = new TabsController()//tabs控制器
@State current: number = 0//当前tab选中项的索引
@Builder //每个tab标签样式布局
tabBuilder($$: BuilderParams) {
Column() {
//图标
Image(this.current === $$.index ? $$.selectIcon : $$.normalIcon).width(26)
//文字
Text($$.label)
.fontSize('12fp')
.fontColor(this.current === $$.index ? '#62C9D0' : '#909090')
.margin({ top: 3 })
}
.width('100%')
.onClick(()=>{
//点击切换页面
this.current=$$.index
this.controller.changeIndex(this.current)//切换到当前页
})
}
说明:通过自定义构建函数tabBuilder绘制了单个标签块内容,垂直容器内添加一个图标和标题,入参包括当前标签索引、标题、激活状态下图标和未激活状态图标。通过入参索引和当前tabbar选中的位置current值判断是否处于选中状态,最后通过TabsController 控制器响应点击事件切换标签页
2.Tabs构建整个TabBar页面级容器
build() {
Column() {
Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
TabContent() {
Text('1')//首页内容
}.tabBar(this.tabBuilder({
index: 0,//索引
label: '首页',//标签
normalIcon: $r('app.media.tabbar11'),//未选中图标
selectIcon: $r('app.media.tabbar12')//选中图标
}))
TabContent() {
Text('2')//发现页内容
}.tabBar(this.tabBuilder({
index: 1,
label: '发现',
normalIcon: $r('app.media.tabbar21'),
selectIcon: $r('app.media.tabbar22')
}))
TabContent() {
Text('3')//购物车页内容
}.tabBar(this.tabBuilder({
index: 2,
label: '购物车',
normalIcon: $r('app.media.tabbar31'),
selectIcon: $r('app.media.tabbar32')
}))
TabContent() {
Text('4')//我的页内容
}.tabBar(this.tabBuilder({
index: 3,
label: '我的',
normalIcon: $r('app.media.tabbar41'),
selectIcon: $r('app.media.tabbar42')
}))
}
.width('100%')
.barMode(BarMode.Fixed)//平均分配barWidth宽度
.scrollable(true) //滑动页面切换tab
//与tabcontent分割线样式
.divider({
color: '#dedede',
strokeWidth: 1
})
.barBackgroundColor(Color.White)
.onChange(((index:number)=>{
this.current=index
}))
}.width('100%')
.backgroundColor('#f2f2f2')
}
说明:Tabs内嵌4个TabContent子组件分别对应标签页内容,TabContent各自加载自定义构建函数绘制底部标签栏样式。示例中Text(‘1’)//首页内容, Text(‘2’)//发现页内容为标签页面显示内容,实际开发中可单独创建组件文件引入开发,方便维护。
完整代码
TabBar.ets
//tabItem自定义构建函数入参类型
interface BuilderParams {
index: number //标签索引
label: string //标签名称
normalIcon: Resource //未选中状态图标
selectIcon: Resource //选中状态图标
}
@Entry
@Component
struct TabBar {
controller: TabsController = new TabsController()//tabs控制器
@State current: number = 0//当前tab选中项的索引
@Builder //每个tabItem样式布局
tabBuilder($$: BuilderParams) {
Column() {
//图标
Image(this.current === $$.index ? $$.selectIcon : $$.normalIcon).width(26)
//文字
Text($$.label)
.fontSize('12fp')
.fontColor(this.current === $$.index ? '#62C9D0' : '#909090')
.margin({ top: 3 })
}
.width('100%')
.onClick(()=>{
this.current=$$.index
this.controller.changeIndex(this.current)//切换到当前页
})
}
build() {
Column() {
Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
TabContent() {
Text('1')//首页内容
}.tabBar(this.tabBuilder({
index: 0,//索引
label: '首页',//标签
normalIcon: $r('app.media.tabbar11'),//未选中图标
selectIcon: $r('app.media.tabbar12')//选中图标
}))
TabContent() {
Text('2')//发现页内容
}.tabBar(this.tabBuilder({
index: 1,
label: '发现',
normalIcon: $r('app.media.tabbar21'),
selectIcon: $r('app.media.tabbar22')
}))
TabContent() {
Text('3')//购物车页内容
}.tabBar(this.tabBuilder({
index: 2,
label: '购物车',
normalIcon: $r('app.media.tabbar31'),
selectIcon: $r('app.media.tabbar32')
}))
TabContent() {
Text('4')//我的页内容
}.tabBar(this.tabBuilder({
index: 3,
label: '我的',
normalIcon: $r('app.media.tabbar41'),
selectIcon: $r('app.media.tabbar42')
}))
}
.width('100%')
.barMode(BarMode.Fixed)//平均分配barWidth宽度
.scrollable(true) //滑动页面切换tab
//与tabcontent分割线样式
.divider({
color: '#dedede',
strokeWidth: 1
})
.barBackgroundColor(Color.White)
.onChange(((index:number)=>{
this.current=index
}))
}.width('100%')
.backgroundColor('#f2f2f2')
}
}
运行效果: