ArkUI自定义TabBar组件

在ArkUI中的Tabs,通过页签进行内容视图切换的容器组件,每个页签对应一个内容视图。其中内容是图TabContent作为Tabs的自组件,通过给TabContent设置tabBar属性来自定义导航栏样式。现在我们就根据UI设计的效果图来实现下图效果:

根据上图分析可知,要实现以上效果需要下面这几步:

1、设置tabBar背景颜色,以及点击选中背景样式;

2、自定义导航栏指示器;

3、设置指示器跟随内容视图一起滑动切换效果。

设置tabBar背景颜色以及点击选中背景样式

1、首先我们需要使用@Builder修饰方法来表示这是一个自定义组件;

2、根据用户点击的tab索引和当前索引来设置背景图片和背景颜色,这里需要注意的是设置背景颜色的时候,注意左上角和右上角是有圆角的,需要根据索引判断是否展示圆角。

3、由于选中样式是带圆角的梯形,所以这里是用来3个不同的梯形切图。

@Builder
tabBuilder(title: string, targetIndex: number, selectImage: ResourceStr) {// 创建一个Column布局Column() {// 创建一个Text组件,显示标题Text(title)// 根据当前索引和目标索引判断字体颜色.fontColor(this.currentIndex === targetIndex ? $r("app.color.text_one") : $r("app.color.text_two"))// 设置字体大小为14.fontSize(14)// 根据当前索引和目标索引判断字体粗细.fontWeight(this.currentIndex === targetIndex ? 600 : 400)}// 设置Column的宽度为100%.width('100%')// 设置Column的高度为100%.height("100%")// 设置Column的子组件垂直居中对齐.justifyContent(FlexAlign.Center)// 根据当前索引和目标索引判断是否设置背景图片.backgroundImage(this.currentIndex == targetIndex ? selectImage : null)// 设置Column的背景颜色.backgroundColor($r("app.color.bg_data_color"))// 根据目标索引判断是否需要设置顶部左右圆角.borderRadius({ topLeft: targetIndex == 0 ? 8 : 0, topRight: targetIndex == 2 ? 8 : 0 })// 设置背景图片填充方式为填充整个容器.backgroundImageSize(ImageSize.FILL)
}

自定义导航栏指示器

由于指示器需要跟随内容视图一起滑动切换,所以指示器不能在单个tabBuilder中设置。

1、使用Column组件定义底部指示器,设置一个宽度为文字宽度,高度为3的蓝色指示器;

2、这里的指示器宽度可以动态设置成文字的宽度,也可以直接设置成文字某个固定宽度;

3、指示器距离左边的距离需要动态设置,配上动画,可以实现指示器跟随手指滑动。

Stack() {Tabs({ barPosition: BarPosition.Start }) {TabContent() {this.tripPage()}.tabBar(this.tabBuilder("房源", 0, $r("app.media.trip_data_start_bg"))).align(Alignment.TopStart).margin({ top: 54 })............}.backgroundColor($r("app.color.white")).borderRadius(8).barHeight(44).width("93.6%").height(380).onChange((index) => {this.currentIndex = index})//自定义指示器,设置一个宽度为文字宽度,高度为3的蓝色指示器Column().width(this.indicatorWidth).height(3).backgroundColor($r("app.color.main_color")).margin({ left: this.indicatorLeftMargin, top: 42 }).borderRadius(1)
}

添加指示器动画

要实现指示器跟随手指滑动,切换不同的tab,需要为指示器添加动画,监听Tabs动画开始和动画结束,以及手势监听。

/*** 启动动画至指定位置** @param duration 动画时长* @param leftMargin 动画结束后的左边距* @param width 动画结束后的宽度*/private startAnimateTo(duration: number, leftMargin: number, width: number) {// 设置动画开始标志为truethis.isStartAnimateTo = trueanimateTo({// 动画时长duration: duration, // 动画时长// 动画曲线curve: Curve.Linear, // 动画曲线// 播放次数iterations: 1, // 播放次数// 动画模式playMode: PlayMode.Normal, // 动画模式// 动画结束时的回调函数onFinish: () => {// 将动画开始标志设置为falsethis.isStartAnimateTo = false// 输出动画结束信息console.info('play end')}}, () => {// 设置指示器的左边距this.indicatorLeftMargin = leftMargin// 设置指示器的宽度this.indicatorWidth = width})}

1、动画开始的监听

Tab切换动画开始时,动画返回的目标索引设置为当前索引,调用startAnimateTo方法,给指示器设置动画,动态设置指示器的左边距。

  Tabs({ barPosition: BarPosition.Start }) {}.onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {// 切换动画开始时触发该回调。指示器跟着页面一起滑动。this.currentIndex = targetIndexthis.startAnimateTo(this.animationDuration, this.textInfos[targetIndex][0], this.textInfos[targetIndex][1])})

2、动画结束的监听

tab切换动画结束时,回触发onAnimationEnd监听。

  Tabs({ barPosition: BarPosition.Start }) {}.onAnimationEnd((index: number, event: TabsAnimationEvent) => {// 切换动画结束时触发该回调。指示器动画停止。let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event)this.startAnimateTo(0, currentIndicatorInfo.left, currentIndicatorInfo.width)})

3、手势滑动监听

在页面跟手滑动过程中,逐帧触发该回调。

 Tabs({ barPosition: BarPosition.Start }) {}
.onGestureSwipe((index: number, event: TabsAnimationEvent) => {// 在页面跟手滑动过程中,逐帧触发该回调。let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event)//设置当前索引this.currentIndex = currentIndicatorInfo.index//设置指示器距离左边间距this.indicatorLeftMargin = currentIndicatorInfo.left//指示器宽度设置this.indicatorWidth = currentIndicatorInfo.width})

封装获取指示器信息方法,返回指示器的索引,左边距和指示器宽度,在手势滑动监听中调用该方法,可以动态获取指示器的左边距,配合动画,可以实现指示器跟随手势滑动。从而实现UI设计效果。

/*** 获取当前指示器信息** @param index 当前索引* @param event Tabs动画事件* @returns 包含指示器索引、左边距和宽度的对象*/private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> {// 当前Tab的索引let nextIndex = index// 如果当前索引大于0且滑动偏移量大于0,表示向左滑动,将nextIndex减1if (index > 0 && event.currentOffset > 0) {nextIndex--}// 如果当前索引小于3且滑动偏移量小于0,表示向右滑动,将nextIndex加1else if (index < 3 && event.currentOffset < 0) {nextIndex++}// 获取当前索引对应的Tab信息let indexInfo = this.textInfos[index]// 获取nextIndex对应的Tab信息let nextIndexInfo = this.textInfos[nextIndex]// 计算滑动比例let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth)// 如果滑动比例大于0.5,则将currentIndex设为nextIndex,表示切换到下一页的tabBar// 页面滑动超过一半,tabBar切换到下一页。let currentIndex = swipeRatio > 0.5 ? nextIndex : index// 根据滑动比例计算当前Tab的左边距let currentLeft = indexInfo[0] + (nextIndexInfo[0] - indexInfo[0]) * swipeRatio// 根据滑动比例计算当前Tab的宽度let currentWidth = indexInfo[1] + (nextIndexInfo[1] - indexInfo[1]) * swipeRatio// 返回包含当前Tab索引、左边距和宽度的对象return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth }}

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

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

相关文章

Python | Leetcode Python题解之第508题出现次数最多的子树元素和

题目&#xff1a; 题解&#xff1a; class Solution:def findFrequentTreeSum(self, root: TreeNode) -> List[int]:cnt Counter()def dfs(node: TreeNode) -> int:if node is None:return 0sum node.val dfs(node.left) dfs(node.right)cnt[sum] 1return sumdfs(r…

设计模式(二)工厂模式详解

设计模式&#xff08;二&#xff09;工厂模式详解 简单工厂模式指由一个工厂对象来创建实例,适用于工厂类负责创建对象较少的情况。例子&#xff1a;Spring 中的 BeanFactory 使用简单工厂模式&#xff0c;产生 Bean 对象。 工厂模式简介 定义&#xff1a;工厂模式是一种创建…

AnaTraf | 全面掌握网络健康状态:全流量的分布式网络性能监测系统

AnaTraf 网络性能监控系统NPM | 全流量回溯分析 | 网络故障排除工具AnaTraf网络流量分析仪是一款基于全流量&#xff0c;能够实时监控网络流量和历史流量回溯分析的网络性能监控与诊断系统&#xff08;NPMD&#xff09;。通过对网络各个关键节点的监测&#xff0c;收集网络性能…

【计算机网络 - 基础问题】每日 3 题(五十七)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…

代谢组数据分析(二十):通过WGCNA识别核心代谢物

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍识别核心基因加载R包导入数据数据预处理检查数据完整性计算软阈值soft根据软阈值构建接矩阵和拓扑重叠矩阵聚类并构建网络拓扑重叠热图查看具体模块的代谢物表达热图识别表型相关模…

sharpkeys-键盘部分按键不好用,用其它不常用按键代替

sharpkeys-键盘部分按键不好用&#xff0c;用其它不常用按键代替 文章目录

linux网络编程5——Posix API和网络协议栈,使用TCP实现P2P通信

文章目录 Posix API和网络协议栈&#xff0c;使用TCP实现P2P通信1. socket()2. bind()3. listen()4. connect()5. accept()6. read()/write(), recv()/send()7. 内核tcp数据传输7.1 TCP流量控制7.2 TCP拥塞控制——慢启动/拥塞避免/快速恢复/快速重传 8. shutdown()9. close()9…

Python游戏开发超详细第二课/一个小游戏等制作过程(入门级篇共2节)

直播内容&#xff0c;这里都用大多用照片代替了哈&#xff0c;因为在写一遍很累&#xff0c;哥哥姐姐理解一下抱歉抱歉 一个是我懒的写一遍&#xff0c;但是刚学的兄弟姐妹可不许学我偷懒哈 二防止有人偷懒&#xff0c;直接复制粘贴代码&#xff0c;所以为了方便帮助你们学习&a…

基于docker 部署redis

1、拉取镜像 docker pull redis:latest如果拉取失败可以尝试下配置镜像源&#xff0c;具体参考如下&#xff0c;目前暂可以使用 Docker切换镜像源-CSDN博客 2、创建配置文件 mkdir /usr/local/redis/conf vim redis.conf bind 0.0.0.0#protected-mode no port 6379 tcp-b…

新手直播方案

简介 新手直播方案 &#xff0c;低成本方案 手机/电脑 直接直播手机软件电脑直播手机采集卡麦电脑直播多摄像机 机位多路采集卡 多路麦加电脑&#xff08;高成本方案&#xff09; 直播推流方案 需要摄像头 方案一 &#xff1a;手机 电脑同步下载 网络摄像头 软件&#xff08…

【南方科技大学】CS315 Computer Security 【Lab6 IoT Security and Wireless Exploitation】

目录 Introduction (Part 1: OS Security for IoT )Software RequirementsStarting the Lab 6 Virtual MachineSetting up the Zephyr Development EnvironmentDownload the Zephyr Source CodeInstalling Requirements and DependenciesSetting the Project’s Environment Va…

【linux】服务器Ubuntu20.04安装cuda11.8教程

【linux】服务器Ubuntu20.04安装cuda11.8教程 文章目录 【linux】服务器Ubuntu20.04安装cuda11.8教程到官网找到对应版本下载链接终端操作cudnn安装到官网下载下载后解压进入解压后的目录&#xff1a;将头文件复制到 /usr/local/cuda/include/ 目录&#xff1a;将库文件复制到 …

利用客户端导入有关联的业务数据(DBeaver+sql)

前言 最近有点坑&#xff0c;麻辣烫的活落手上了&#xff0c;上个迭代除了自己的开发任务&#xff0c;还有处理接手的工作。然后节后问题又多&#xff0c;还有前1个迭代没有测试的模块本迭代测试&#xff0c;烦死了。 这次这个数据处理的活&#xff0c;以后希望可以交出…

mac电脑设置chrome浏览器语言切换为日语英语等不生效问题

在chrome中设置了语言&#xff0c;并且已经置顶了&#xff0c;但是不生效&#xff0c;在windows上直接有设置当前语言为chrome显示语言&#xff0c;但是mac上没有。 解决办法 在系统里面有一个单独给chrome设置语言的&#xff1a; 单独给它设定成指定的语言&#xff0c;然后重…

川渝地区计算机考研择校分析

C哥专业提供——计软考研院校选择分析专业课备考指南规划 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 根据最新数据分析,5所高校计算机专业2025年考研难度从高到低预计为: 电子科技大学 >> 四川大学 > 重庆大学 ≈ 西南交通大学 > 西南…

Vision-Language Models for Vision Tasks: A Survey阅读笔记

虽然LLM的文章还没都看完&#xff0c;但是终究是开始看起来了VLM&#xff0c;首当其冲&#xff0c;当然是做一片文献综述啦。这篇文章比较早了&#xff0c;2024年2月份出的last version。 文章链接&#xff1a;https://arxiv.org/abs/2304.00685 GitHub链接&#xff1a;GitHu…

命名空间std, using namespace std

命名空间std&#xff0c;using namespace std 在标准C以前&#xff0c;都是用#include<iostream.h>这样的写法的&#xff0c;因为要包含进来的头文件名就是iostream.h。标准C引入了名字空间的概念&#xff0c;并把iostream等标准库中的东东封装到了std名字空间中&#x…

【linux网络编程】| 网络基础 | 解析IP与Mac地址的区别

前言&#xff1a;本节内容讲解一些网络基础相关的知识点&#xff0c; 不涉及网络代码&#xff01;同样的本节内容是作为前一篇的补充知识点&#xff0c; 前一篇文章地址&#xff1a;【linux网络编程】 | 网络基础Ⅰ| 认识网络-CSDN博客&#xff0c;本篇文章内容较少&#xff0c…

【论文笔记】MLSLT: Towards Multilingual Sign Language Translation

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: MLSLT: Towards Multiling…

计算机网络:网络层 —— IPv4 协议的表示方法及其编址方法

文章目录 IPv4IPv4的表示方法IPv4的编址方法分类编址A类地址B类地址C类地址可指派的地址数量一般不使用的特殊IPv4地址 划分子网编址子网掩码默认子网掩码 无分类编址方法地址掩码斜线记法无分类域间路由选择 CIDR IPv4 IPv4&#xff08;Internet Protocol version 4&#xff…