鸿蒙ArkUI开发-实现增删Tab页签

场景介绍

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

效果呈现

如下动图所示:

环境要求

本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:

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

实现原理

本例涉及的关键特性以及实现方案如下:

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

开发步骤

  1. 整体布局分为两部分:页面内容和页签部分。页面视图部分使用Tabs,其中页签对应显示的内容需要放入TabContent组件中。页签部分通过@Builder自定义封装一个tabBar组件,放入Tabs中的tabBar属性中。

    页面视图代码块:

    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代码块:

    //控制页签渲染的数组
    @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)}.justifyContent(FlexAlign.Center).constraintSize({ minWidth: 35 }).width(120).height(30).borderRadius({ topLeft: 10, topRight: 10 }).backgroundColor(this.tabArray.indexOf(tabNumber) === this.focusIndex ? "#ffffffff" : "#ffb7b7b7")
    }
  2. 实现页签和页面视图的联动:这里主要通过TabsController的changeIndex来实现对应的试图跳转,但需要注意由于之后会增删数组元素的操作,所以此处传入的index值是选择页面的tabNumber在数组中的索引值。

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

    this.tabArray.push(this.tabArray[this.tabArray.length - 1] + 1)
  4. 通常在添加新页面后,浏览器会将页面跳转到新添加的页面,因此在向tabArray中完成数据推送后,需要将页签通过TabsController中的changeIndex方法跳转到最后一个。

    this.focusIndex = this.tabArray.length - 1
    this.controller.changeIndex(this.focusIndex)
  5. 当用户选择另一个页签时,可通过自定义页签中通用事件onClick进行控制,当用户点击待选择的页签后,获取当前页签对应的下标,然后通过TabsController中的changeIndex方法进行跳转,此外可以通过更改页签背景颜色标识被选中页签的。

    .backgroundColor(this.tabArray.indexOf(tabNumber) === this.focusIndex ? "#ffffffff" : "#ffb7b7b7")
    .onClick(() => {this.focusIndex = this.tabArray.indexOf(tabNumber)this.controller.changeIndex(this.focusIndex)
    })
  6. 删除页面有三种情况(删除不同位置的页面)。

    • 第一种情况是被关闭页面为最后一个页面,且当前选中的页面为最后一个页面,如果当前被选中页面是刚刚被其他页面创建的情况,因此页面需要跳回到创建被删除页签的页签(逻辑参考chrome浏览器)

      if (this.pre >= 0) {this.focusIndex = this.pre
      } 
      this.tabArray.splice(this.tabArray.indexOf(tabNumber), 1)

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

      this.focusIndex = this.focusIndex - 1
      this.tabArray.splice(this.tabArray.indexOf(tabNumber), 1)
      this.pre = -1
      this.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
@Component
struct Drag {//控制页签渲染的数组@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).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 }) {//tabsTabs({ 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.focusIndexthis.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%')}
}

本文主要讲解在鸿蒙开发当中ArkUI中的增删Tab页签实现,更多的鸿蒙ArkUI学习,可以参考下面的学习曲线图:

完整版鸿蒙os与openharmony技术路线与文档,可在主页保存

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/248952.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python数据类型-列表

1 python中列表的定义 python中列表是一种有序和可更改的集合&#xff0c;允许重复的成员&#xff0c;列表中的元素之间数据类型可以不同&#xff08;元素之间数据类型可以不相同&#xff0c;这一点和其它的面向对象的开发语言有很大的不同&#xff0c;如C#、Java&#xff09;…

UI设计行业的就业前景如何?

UI设计师的就业前景是许多新人进入这个行业之前关注的问题之一。在数字时代&#xff0c;用户界面设计在各个行业发挥着越来越重要的作用。优秀的UI设计师需要优化他们的产品和服务&#xff0c;无论是互联网公司、软件开发公司、医疗器械、汽车制造等传统行业。本文将从市场需求…

基于Javaweb开发的二手图书零售系统详细设计【附源码】

基于Javaweb开发的二手图书零售系统详细设计【附源码】 &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制系统…

【Vue】前端项目引入阿里图标

【Vue&React】前端项目引入阿里图标 方式11、登录自己的iconfont-阿里巴巴矢量图标库&#xff0c;把需要的图标加入到自己的项目中去&#xff1b;2、加入并进入到项目中去选择Font class 并下载到本地3、得到的文件夹如下4. 把红框中的部分粘贴到自己的项目中&#xff08; …

【HarmonyOS 4.0 应用开发实战】TypeScript入门之接口详讲

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

接口的多继承

接口的多继承也就是一个接口可以同时继承多个接口&#xff0c;也是使用extends关键字实现。 接口的多继承的好处&#xff1a;假如有一个实现类要继承三个接口&#xff0c;现在只需要将多个接口“合并”为一个接口&#xff0c;就可以便于实现类实现。 注意&#xff1a;1.如果一…

为什么要用云手机养tiktok账号

在拓展海外电商市场的过程中&#xff0c;许多用户选择采用tiktok短视频平台引流的策略&#xff0c;以提升在电商平台上的流量&#xff0c;吸引更多消费者。而要进行tiktok引流&#xff0c;养号是必不可少的一个环节。tiktok云手机成为实现国内跨境养号的一种有效方式&#xff0…

Kafka-服务端-PartitionLeaderSelector、ReplicaStateMachine

PartitionLeaderSelector 通过对前面的分析可知&#xff0c;PartitionMachine将Leader副本选举、确定ISR集合的工作委托给了PartitionLeaderSelector接口实现&#xff0c;PartitionMachine可以专注于管理分区状态。这是策略模式的一种典型的应用场景。 图展示了PartitionLead…

WINDOWS中电源设置小工具

你可以使用WinPowerSet&#xff0c;玩CS2之前&#xff0c;把电源设置为“高性能”&#xff0c;玩后设置为“平衡”。 WinPowerSet 下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1iOp29c4ica9L47t_l9lZ2w?pwdd248 提取码&#xff1a;d248 最近新配了一台12…

vue使用富文本

1、安装 cnpm install vue-quill-editor2、在main.js中引入 // 富文本 import VueQuillEditor from vue-quill-editor // require styles 引入样式 import quill/dist/quill.core.css import quill/dist/quill.snow.css import quill/dist/quill.bubble.css Vue.use(VueQuill…

Roxlabs全球IP代理服务:解锁高效数据采集与网络应用新境界

引言 在这个数字化迅速发展的时代&#xff0c;数据采集和网络应用的重要性显得愈发突出。江苏阿克索网络科技有限公司旗下的Roxlabs&#xff0c;以其卓越的全球IP代理服务&#xff0c;正引领着这一领域的创新和发展。Roxlabs不仅提供遍及200多个国家和地区的高质量动态住宅IP资…

Linux下使用信号量实现PV操作

一.信号量与PV操作概述 在多道程序系统中&#xff0c;由于资源共享与进程合作&#xff0c;使各进程之间可能产生两种形式的制约关系&#xff0c;一种是间接相互制约&#xff0c;例如&#xff0c;在仅有一台打印机的系统&#xff0c;同一时刻只能有一个进程分配到到打印机&…

ONLYOFFICE | 免费开源办公神器新选择

目录 前言&#xff1a; 1、什么是ONLYOFFICE&#xff1f; 2、ONLYOFFICE下载使用 3、ONLYOFFICE团队协作云办公功能 4、ONLYOFFICE 8.0新版本的亮点功能 4.1、显示协作者头像 4.2、插件 UI 界面更新 4.3、可填写的 PDF 表单 5、最后 前言&#xff1a; 一个好的开发工具…

(五)MySQL的备份及恢复

1、MySQL日志管理 在数据库保存数据时&#xff0c;有时候不可避免会出现数据丢失或者被破坏&#xff0c;这样情况下&#xff0c;我们必须保证数据的安全性和完整性&#xff0c;就需要使用日志来查看或者恢复数据了 数据库中数据丢失或被破坏可能原因&#xff1a; 误删除数据…

PyTorch深度学习实战(34)——Pix2Pix详解与实现

PyTorch深度学习实战&#xff08;34&#xff09;——Pix2Pix详解与实现 0. 前言1. 模型与数据集1.1 Pix2Pix 基本原理1.2 数据集分析1.3 模型构建策略 2. 实现 Pix2Pix 生成图像小结系列链接 0. 前言 Pix2Pix 是基于生成对抗网络 (Convolutional Generative Adversarial Netwo…

[数据结构]-哈希

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 本期学习目标&…

Golang 流媒体服务器lalserver使用指南

目录 安装 使用 1.推流 2.播放 官方地址 安装 1.下载源码 wget https://github.com/q191201771/lal/releases/download/v0.36.7/lal_v0.36.7_linux.zipunzip lal_v0.36.7_linux.zip cd lal_v0.36.7_linux 2.启动 ./bin/lalserver -c ./conf/lalserver.conf.json 使用 …

蓝桥杯---牌型种数

小明被劫持到X赌城&#xff0c;被迫与其他3人玩牌。一副扑克牌(去掉大小王牌,共52张)&#xff0c;均匀发给4个人&#xff0c;每个人13张。这时&#xff0c;小明脑子里突然冒出一个问题&#xff1a;如果不考虑花色&#xff0c;只考虑点数&#xff0c;也不考虑自己得到的牌的先后…

WSL2+ubuntu 18+VsCode 配置C/C++开发环境 踩坑

1. 管理员模式打开cmd&#xff0c;或PowerShell &#xff0c;输入 wsl --install 可能出现的错误&#xff1a;无法解析服务器名称或地址 解决方式&#xff1a;科学上网 安装WSL时遇到“无法解析服务器名称或地址”的错误及解决方法 - 知乎 错误2&#xff1a;Error 0x8037…

黑马程序员前端web入门:新浪新闻

黑马程序员前端web入门&#xff1a;新浪新闻 几点学习到的&#xff1a; 设置li无圆点: list-style: none;设置a无下划线&#xff1a;text-decoration: none;a属于行内元素&#xff0c;高度hegiht不起作用&#xff0c;可以设置 display: block; 把它变成块元素。此时&#xff0c…