HarmonyOS 开发——自定义tabs导航栏实现切换效果

一.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%')
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值