一.Tabs组件的子组件TabContent有属性.tabBar(),由官方文档可知,其参数value: 可以是字符串、资源引用、自定义构建器或包含 icon 和 text 的对象,用于设置 TabBar 上显示的内容。
但是该导航栏为默认样式,要想实现更多样式,必须自定义TabBar。
自定义TabBar步骤:1.使用@Builder修饰一个函数tabBarBuilder(),里面是自定义的TabBar的结构。2..tabBar()中传参为自定义构建器this.tabBarBuilder()。注:构建器函数里面是可以传参的,所以多个TabContent可以共用一个tabBarBuilder()然后传不同的参数。
二.在自定义TabBar的过程中,我们发现会遇到切换TabContent时,导航栏发生样式变化的场景。
.onChange()事件和.onTabBarClick()事件都可以获得当前页面的索引。所以设置一个存储当前页面的索引值的变量,在触发事件的时候,将获得的索引值存储到这个变量中即可。
//设置当前页面的索引值,默认为0
@State currentIndex: number = 0
//设置onChange事件
Tabs(){}
.onChange((index: number) => {
this.currentIndex = index
})
三.在第一点我们提到构建器函数里面是可以传参的,如果我们在传参中加上每个TabContent对应的下标值,那么在自定义的tabBar中,就可以通过判断语句来设置不同TabContent的不同样式。还可以通过传参的下标值与第二点中变量存储的索引值比较,从而设置当前页面的高亮显示(通过三元符)。
四.轮播图
@Builder
//轮播图Builder
pic(url: ResourceStr, str: string) {
Column() {
Image(url)
.width('100%')
Row() {
Text(str)
.fontColor(Color.White)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.maxLines(1)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
}
.width('100%')
.padding({
left: 10,
right: 10
})
.position({
bottom: 30
})
}
.width('100%')
}
//轮播图List
@State bannerList: BannerItem[] = [
{ imgUrl: $r('app.media.banner_01_1'), imgTitle: '谷歌杀入开源大模型,凭什么问鼎王座?' },
{ imgUrl: $r('app.media.banner_01_3'), imgTitle: '微软牵手OpenAI劲敌!Mistral顶级大模型不再开源' },
{ imgUrl: $r('app.media.banner_01_4'), imgTitle: '生成式AI会扼杀DevSecOps吗' },
{ imgUrl: $r("app.media.banner_01_2"), imgTitle: '微软继续Linux化!sudo原生命令强势登录Windows' },
]
//轮播图
Swiper() {
ForEach(this.bannerList, (item: BannerItem) => {
this.pic(item.imgUrl, item.imgTitle)
})
}
.width('100%')
.loop(true)
.autoPlay(true)
.indicator(
new DotIndicator()
.itemWidth(30)
.itemHeight(5)
.selectedItemWidth(30)
.selectedItemHeight(5)
.color(Color.Gray)
.selectedColor(Color.White)
.left(30)
)
五.滑动栏
@Builder
//滑动栏Builder
nav(url: ResourceStr, str: string) {
Column() {
Image(url)
.width(40)
Text(str)
.fontSize(14)
.fontColor('#666666')
}
.justifyContent(FlexAlign.SpaceBetween)
.height('100%')
}
//滑动栏List
@State columnarList: ColumnarItem[] = [
{ iconUrl: $r('app.media.ic_01_1'), iconTitle: '内容精选' },
{ iconUrl: $r('app.media.ic_01_2'), iconTitle: '学堂' },
{ iconUrl: $r('app.media.ic_01_3'), iconTitle: '鸿蒙开发者社区' },
{ iconUrl: $r('app.media.ic_01_4'), iconTitle: '博客' },
{ iconUrl: $r('app.media.ic_01_5'), iconTitle: '企业培训' },
{ iconUrl: $r('app.media.ic_01_6'), iconTitle: 'Next训练营' },
{ iconUrl: $r('app.media.ic_01_7'), iconTitle: '精培' },
]
//滑动栏
Grid() {
ForEach(this.columnarList, (item: ColumnarItem) => {
GridItem() {
this.nav(item.iconUrl, item.iconTitle)
}
.height(65)
})
}
.scrollBar(BarState.Off)
.height(65)
.columnsGap(24)
.rowsTemplate('1fr')
.margin({
top: 10,
left: 12,
right: 12
})
Case:
interface BannerItem {
imgUrl: ResourceStr,
imgTitle: string
}
interface ColumnarItem {
iconUrl: ResourceStr,
iconTitle: string
}
interface RecommendItem {
imgUrl: ResourceStr,
title: string,
createTime: string
}
@Entry
@Component
struct Index {
//设置当前页面的索引值,默认为0
@State currentIndex: number = 0
@Builder
tabBarBuilder(str: string, index: number) {
if (index == 2) {
Badge({
count: 9,
style: {
badgeSize: 16,
badgeColor: '#f00',
fontSize: 14
}
}) {
Text(str)
.fontSize(18)
.fontColor(this.currentIndex == index ? Color.Black : '#ccc')
.fontWeight(this.currentIndex == index ? FontWeight.Bold : FontWeight.Normal)
.padding(5)
}
} else {
if (this.currentIndex == 1) {
Text(str)
.fontSize(18)
.fontColor(this.currentIndex == index ? Color.White : '#ccc')
.fontWeight(this.currentIndex == index ? FontWeight.Bold : FontWeight.Normal)
} else {
Text(str)
.fontSize(18)
.fontColor(this.currentIndex == index ? Color.Black : '#ccc')
.fontWeight(this.currentIndex == index ? FontWeight.Bold : FontWeight.Normal)
}
}
}
//轮播图List
@State bannerList: BannerItem[] = [
{ imgUrl: $r('app.media.banner_01_1'), imgTitle: '谷歌杀入开源大模型,凭什么问鼎王座?' },
{ imgUrl: $r('app.media.banner_01_3'), imgTitle: '微软牵手OpenAI劲敌!Mistral顶级大模型不再开源' },
{ imgUrl: $r('app.media.banner_01_4'), imgTitle: '生成式AI会扼杀DevSecOps吗' },
{ imgUrl: $r("app.media.banner_01_2"), imgTitle: '微软继续Linux化!sudo原生命令强势登录Windows' },
]
//滑动栏List
@State columnarList: ColumnarItem[] = [
{ iconUrl: $r('app.media.ic_01_1'), iconTitle: '内容精选' },
{ iconUrl: $r('app.media.ic_01_2'), iconTitle: '学堂' },
{ iconUrl: $r('app.media.ic_01_3'), iconTitle: '鸿蒙开发者社区' },
{ iconUrl: $r('app.media.ic_01_4'), iconTitle: '博客' },
{ iconUrl: $r('app.media.ic_01_5'), iconTitle: '企业培训' },
{ iconUrl: $r('app.media.ic_01_6'), iconTitle: 'Next训练营' },
{ iconUrl: $r('app.media.ic_01_7'), iconTitle: '精培' },
]
@State recommendList: RecommendItem[] = [
{
imgUrl: $r('app.media.list_02_1'),
title: '字节一面:TCP和UDP可以使用同一个端口号吗?',
createTime: '2024-03-05 10:07:22'
},
{
imgUrl: $r('app.media.list_02_2'),
title: 'NoSQL:在高并发场景下,数据库和NoSQL如何做到互补?',
createTime: '2024-03-05 10:03:17'
},
{
imgUrl: $r('app.media.list_02_3'),
title: '处理大规模并发请求时如何设计和优化Python后端服务的架构和性能',
createTime: '2024-03-05 10:03:09'
},
{
imgUrl: $r('app.media.list_02_4'),
title: 'C++右值引用:解锁高效内存管理与性能优化的奥秘',
createTime: '2024-03-05 09:55:00'
},
{ imgUrl: $r('app.media.list_02_5'), title: '现代分布式系统架构的权衡分析', createTime: '2024-03-05 09:52:57' },
]
scroller: Scroller = new Scroller()
@Builder
//轮播图Builder
pic(url: ResourceStr, str: string) {
Column() {
Image(url)
.width('100%')
Row() {
Text(str)
.fontColor(Color.White)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.maxLines(1)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
}
.width('100%')
.padding({
left: 10,
right: 10
})
.position({
bottom: 30
})
}
.width('100%')
}
@Builder
//滑动栏Builder
nav(url: ResourceStr, str: string) {
Column() {
Image(url)
.width(40)
Text(str)
.fontSize(14)
.fontColor('#666666')
}
.justifyContent(FlexAlign.SpaceBetween)
.height('100%')
}
build() {
Column() {
Stack({ alignContent: Alignment.Bottom }) {
if (this.currentIndex == 1) {
Image($r('app.media.bg_02_2'))
.width('100%')
.zIndex(-1)
} else {
Image($r('app.media.bg_02_1'))
.width('100%')
.zIndex(-1)
}
Tabs() {
TabContent() {
Column() {
Row() {
Image($r('app.media.hot_01'))
.width(60)
Row({ space: 15 }) {
Image($r('app.media.user_01'))
.width(20)
Image($r('app.media.more_01'))
.width(20)
}
.height('100%')
}
.width('100%')
.height(56)
.padding({
left: 28,
right: 16
})
.justifyContent(FlexAlign.SpaceBetween)
Scroll() {
Column() {
//轮播图
Swiper() {
ForEach(this.bannerList, (item: BannerItem) => {
this.pic(item.imgUrl, item.imgTitle)
})
}
.width('100%')
.loop(true)
.autoPlay(true)
.indicator(
new DotIndicator()
.itemWidth(30)
.itemHeight(5)
.selectedItemWidth(30)
.selectedItemHeight(5)
.color(Color.Gray)
.selectedColor(Color.White)
.left(30)
)
//滑动栏
Grid() {
ForEach(this.columnarList, (item: ColumnarItem) => {
GridItem() {
this.nav(item.iconUrl, item.iconTitle)
}
.height(65)
})
}
.scrollBar(BarState.Off)
.height(65)
.columnsGap(24)
.rowsTemplate('1fr')
.margin({
top: 10,
left: 12,
right: 12
})
Column() {
Row() {
Text('热门推荐')
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.margin({
top: 30
})
ForEach(this.recommendList, (item: RecommendItem) => {
Row() {
Image(item.imgUrl)
.height('100%')
.borderRadius(8)
.margin({
right: 10
})
Column() {
Text(item.title)
.width('100%')
.maxLines(2)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
Text(item.createTime)
.fontSize(12)
.fontColor('#ccc')
.width('100%')
}
.height('100%')
.justifyContent(FlexAlign.SpaceBetween)
.layoutWeight(1)
}
.margin({
top: 10,
bottom: 10
})
.width('100%')
.height(100)
})
}
.width('100%')
.padding({
left: 10,
right: 10
})
}
.width('100%')
.margin({
bottom: 55
})
}
.width('100%')
.scrollBar(BarState.Off)
}
.width('100%')
.height('100%')
}
.tabBar(this.tabBarBuilder('首页', 0))
TabContent() {
Image($r('app.media.banner_01_4'))
}
.tabBar(this.tabBarBuilder('视频', 1))
TabContent() {
Text('待完成--消息')
}
.tabBar(this.tabBarBuilder('消息', 2))
TabContent() {
Text('我的')
}
.tabBar(this.tabBarBuilder('我的', 3))
}
.barPosition(BarPosition.End)
.onChange((index: number) => {
this.currentIndex = index
})
}
.width('100%')
}
.height('100%')
.width('100%')
}
}