当前位置:首页 > 科技  > 软件

ArkUI如何实现增删Tab页签

来源: 责编: 时间:2024-05-16 09:00:50 193观看
导读部分应用在使用过程中需要自定义添加或删除标签的场景,比如在浏览器中的顶部标签栏中需要新打开或关闭网页页签,而这种场景与Tabs组件效果类似,但Tabs组件不提供增加或删除页签的功能,不能自由的增加删除页签,需要开发者自

G7B28资讯网——每日最新资讯28at.com

部分应用在使用过程中需要自定义添加或删除标签的场景,比如在浏览器中的顶部标签栏中需要新打开或关闭网页页签,而这种场景与Tabs组件效果类似,但Tabs组件不提供增加或删除页签的功能,不能自由的增加删除页签,需要开发者自己实现Tabs中增删页签功能。本文以浏览器中增加或删除页签为例,实现Tabs中页签的增删功能。G7B28资讯网——每日最新资讯28at.com

效果呈现

如下动图所示:G7B28资讯网——每日最新资讯28at.com

图片G7B28资讯网——每日最新资讯28at.com

环境要求

本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:G7B28资讯网——每日最新资讯28at.com

  • IDE: DevEco Studio 4.0 Release
  • SDK: Ohos_sdk_public 4.0.10.13 (API Version 10 Release)

实现原理

本例涉及的关键特性以及实现方案如下:G7B28资讯网——每日最新资讯28at.com

  • 通过@Builder自定义封装一个导航页签栏,并通过ForEach完成对Tabs组件的内容页和导航页签栏的动态渲染。
  • 通过TabsController的changeIndex可实现页面的跳转,传入的index是数组中对应的索引值。
  • 页签的增加通过数组的push方法,增加数组元素。
  • 删除页签通过通过删除页面对应数组的索引值处的数据完成,删除后页面跳转位置根据业务逻辑要求确定跳转页面对应的索引值。

开发步骤

整体布局分为两部分:页面内容和页签部分。页面视图部分使用Tabs,其中页签对应显示的内容需要放入TabContent组件中。页签部分通过@Builder自定义封装一个tabBar组件,放入Tabs中的tabBar属性中。G7B28资讯网——每日最新资讯28at.com

页面视图代码块:G7B28资讯网——每日最新资讯28at.com

Tabs({ barPosition: BarPosition.Start, controller: this.controller }) {  ForEach(this.tabArray, (item: number) => {    TabContent() {      Text('我是页面 ' + item + " 的内容")        .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代码块:G7B28资讯网——每日最新资讯28at.com

//控制页签渲染的数组@State tabArray: Array<number> = [0]//Tabs组件当前显示的页签@State focusIndex: number = 0//创建页签时的页签index@State pre: number = -1//Tabs组件控制器,根据组件下标控制tab跳转private controller: TabsController = new TabsController()//自定义导航页签栏@BuilderTab(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")}

实现页签和页面视图的联动:这里主要通过TabsController的changeIndex来实现对应的试图跳转,但需要注意由于之后会增删数组元素的操作,所以此处传入的index值是选择页面的tabNumber在数组中的索引值。G7B28资讯网——每日最新资讯28at.com

this.focusIndex = this.tabArray.indexOf(tabNumber)

增添数组元素实现增加页签的效果:增添数组元素使用数组的push方法在tabArray添加数据即可,但由于此demo原始定义的数组是连续的自然数,后续增删数组会打乱原有顺序,所以此处处理为先判断最后一个元素的值再加1,由于TabBar的渲染是通过ForEach被@State修饰的数组,因此当tabArray中添加数据后系统会通知ForEach便利数组重新渲染页面。G7B28资讯网——每日最新资讯28at.com

this.tabArray.push(this.tabArray[this.tabArray.length - 1] + 1)

通常在添加新页面后,浏览器会将页面跳转到新添加的页面,因此在向tabArray中完成数据推送后,需要将页签通过TabsController中的changeIndex方法跳转到最后一个。G7B28资讯网——每日最新资讯28at.com

this.focusIndex = this.tabArray.length - 1this.controller.changeIndex(this.focusIndex)

当用户选择另一个页签时,可通过自定义页签中通用事件onClick进行控制,当用户点击待选择的页签后,获取当前页签对应的下标,然后通过TabsController中的changeIndex方法进行跳转,此外可以通过更改页签背景颜色标识被选中页签的。G7B28资讯网——每日最新资讯28at.com

.backgroundColor(this.tabArray.indexOf(tabNumber) === this.focusIndex ? "#ffffffff" : "#ffb7b7b7").onClick(() => {  this.focusIndex = this.tabArray.indexOf(tabNumber)  this.controller.changeIndex(this.focusIndex)})

删除页面有三种情况(删除不同位置的页面)。G7B28资讯网——每日最新资讯28at.com

  • 第一种情况是被关闭页面为最后一个页面,且当前选中的页面为最后一个页面,如果当前被选中页面是刚刚被其他页面创建的情况,因此页面需要跳回到创建被删除页签的页签(逻辑参考chrome浏览器)
if (this.pre >= 0) {  this.focusIndex = this.pre} this.tabArray.splice(this.tabArray.indexOf(tabNumber), 1)

如果不是的话直接将当前显示页签下下标设置为前一个页签下标,tabArray数组通过splice方法删除页签,并通过TabsController完成跳转,此外页面只要有关闭操作,页面就不可以跳回打开该页面的页面,即将 pre设置为-1。G7B28资讯网——每日最新资讯28at.com

this.focusIndex = this.focusIndex - 1this.tabArray.splice(this.tabArray.indexOf(tabNumber), 1)this.pre = -1this.controller.changeIndex(this.focusIndex)
  • 第二种情况,当用户当前选择的不是最后一个标签,然后直接删除其他页签时,可以直接删除删除,但是需要重新计算当前选中页签在tabArray中的实时位置,到新注意需要排除用户在最后一个页签删除当前页签的情况。
//当前选择页面的对应数组中的数据值let focusNumber = this.tabArray[this.focusIndex]//用于判断是否是用户在最后一个页签删除当前页签的情况if (this.tabArray.indexOf(focusNumber) >= 0) {  this.focusIndex = this.tabArray.indexOf(focusNumber)}this.controller.changeIndex(this.focusIndex)
  • 第三种情况,当用户当前选择的不是最后一个标签,且删除被选中页面,直接删除,然后通过TabsController完成跳转,不需要额外操作。

说明

  • 由于Tabs组件中的导航页签栏会占满屏幕,导致添加按钮无法直接添加到与页签直接平齐的位置,因此通过层叠布局(Stack)的方式,将添加页签按钮覆盖到Tabs组件上,通过Stack中的对齐方式将添加按钮调整到合适位置。
  • 用于Tabs添加或删除子节点时,ForEach需要重新将所有页签进行重新渲染,如果在添加或删除完页签后直接调用TabsController中的changeIndex进行跳转,页面无法调到指定页签。这是由于ForEach还未将组件渲染完成,将子组件挂载到Tabs中,因此建议通过setTimeOut延迟一段时间再进行跳转,经过验证大概50ms后即可,开发者可再自行验证。
setTimeout(() => {  this.controller.changeIndex(this.focusIndex)}, 50)

完整实例

完整示例代码如下:

import util from '@ohos.util'@Entry@Componentstruct Drag {  //控制页签渲染的数组  @State tabArray: Array<number> = [0]  //Tabs组件当前显示的页签下标  @State focusIndex: number = 0  //创建页签时的页签index  @State pre: number = -1  //Tabs组件控制器,根据组件下标控制tab跳转  private controller: TabsController = new TabsController()  // 单独的页签  @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组件当前显示的页签中显示的数字        let focusNumber = this.tabArray[this.focusIndex]        //被删除的页签是否是当前选中的页签,且是最后一个页签        if (this.focusIndex === this.tabArray.indexOf(tabNumber) && this.focusIndex == this.tabArray.length - 1) {          //判断是否需要跳回到创建该页签时的页签,如果不需要直接跳转到前一个页签          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        //对应删除页面中的第二种情况        if (this.tabArray.indexOf(focusNumber) >= 0) {          this.focusIndex = this.tabArray.indexOf(focusNumber)        }        //设置50ms 延迟跳转        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 + " 的内容")                    .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%')  }}

本文链接:http://www.28at.com/showinfo-26-88313-0.htmlArkUI如何实现增删Tab页签

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: SpringBoot项目中这10个开发技巧你都知道吗?

下一篇: 盘点GoLang中的三方库:fsnotify、Viper、Logrus、Carbon

标签:
  • 热门焦点
  • 5月iOS设备性能榜:M1 M2依旧是榜单前五

    和上个月一样,没有新品发布的iOS设备性能榜的上榜设备并没有什么更替,仅仅只有跑分变化而产生的排名变动,刚刚开始的苹果WWDC2023,推出的产品也依旧是新款Mac Pro、新款Mac Stu
  • 线程通讯的三种方法!通俗易懂

    线程通信是指多个线程之间通过某种机制进行协调和交互,例如,线程等待和通知机制就是线程通讯的主要手段之一。 在 Java 中,线程等待和通知的实现手段有以下几种方式:Object 类下
  • 三言两语说透设计模式的艺术-单例模式

    写在前面单例模式是一种常用的软件设计模式,它所创建的对象只有一个实例,且该实例易于被外界访问。单例对象由于只有一个实例,所以它可以方便地被系统中的其他对象共享,从而减少
  • 十个简单但很有用的Python装饰器

    装饰器(Decorators)是Python中一种强大而灵活的功能,用于修改或增强函数或类的行为。装饰器本质上是一个函数,它接受另一个函数或类作为参数,并返回一个新的函数或类。它们通常用
  • 微软邀请 Microsoft 365 商业用户,测试视频编辑器 Clipchamp

    8 月 1 日消息,微软近日宣布即将面向 Microsoft 365 商业用户,开放 Clipchamp 应用,邀请用户通过该应用来编辑视频。微软于 2021 年收购 Clipchamp,随后开始逐步整合到 Microsof
  • 阿里大调整

    来源:产品刘有媒体报道称,近期淘宝天猫集团启动了近年来最大的人力制度改革,涉及员工绩效、层级体系等多个核心事项,目前已形成一个初步的&ldquo;征求意见版&rdquo;:1、取消P序列
  • 8月见!小米MIX Fold 3获得3C认证:支持67W快充

    这段时间以来,包括三星、一加、荣耀等等有不少品牌旗下的最新折叠屏旗舰都得到了不少爆料,而小米新一代折叠屏旗舰——小米MIX Fold 3此前也屡屡被传
  • 三星显示已开始为AR设备研发硅基LED微显示屏

    7月18日消息,据外媒报道,随着苹果首款头显产品Vision Pro在6月份正式推出,AR/VR/MR等头显产品也就将成为各大公司下一个重要的竞争领域,对显示屏这一关
  • 质感不错!OPPO K11渲染图曝光:旗舰IMX890传感器首次下放

    一直以来,OPPO K系列机型都保持着较为均衡的产品体验,历来都是2K价位的明星机型,去年推出的OPPO K10和OPPO K10 Pro两款机型凭借各自的出色配置,堪称有
Top