鸿蒙自定义Tab,可居左显示

news2024/11/15 13:52:55

最近写鸿蒙项目时,需要用到类似Android的TabLayout控件,鸿蒙官方也有提供类似实现的组件Tabs。但是官方Tabs组件,实在有点鸡肋,首先 TabContent和 TabBar是绑定在一起的放在Tabs里面的,如果UI是TabBar的背景是一个整体的,这时就受限了。另一个最令人吐槽的是,这个TabBar无法设置居左显示,它是默认居中的。
好吧,既然官方没有,就只能自己手动去实现了。
先看下效果:
在这里插入图片描述

一、首先,需要分析Tab组件点击滚动时的交互, 需要把目标Tab滚动到屏幕中间为止,那么就要计算 组件中线与屏幕中间的位置,从而得出需要滚动的距离,看下图会比较直观

在这里插入图片描述
那么就要首先要保存好每个Tab的坐标信息:

 @State tabDatas: Array<string> = []

// tab : 保存好每个Tab的坐标信息
 .onAreaChange((old, newArea) => {
              this.tabAreas.set(index, newArea)
            })
            
// scroll: 获取scroll组件的中线位置(屏幕的中线x坐标)
 .onAreaChange((oldValue, newValue) => {
        this.scrollerMiddleX = (newValue.width as number) / 2
      })

然后拿中线x坐标减去屏幕的中线x坐标 得到的就是 Tab需要滚动中间距离:

 const selectedArea = this.tabAreas.get(index)
 const targetMiddleX = (selectedArea.width as number) / 2 + (selectedArea.globalPosition.x as number)
 const scrollOffsetX = targetMiddleX - this.scrollerMiddleX //滚动中间距离
 this.scroller.scrollTo({
      xOffset: this.scroller.currentOffset().xOffset + scrollOffsetX,//使用scrollTo,所以需要加上原有的偏移位置
      yOffset: 0,
      animation: { duration: 500 }
    })
 

至此,滚动功能就已经实现了。

二、然后就到Item组件的实现了,因为组件的样式是不确定的,需要交由外部去实现,这时就需要用到@BuilderParam了。
还有注意@BuilderParam如果需要按引用传递,需要把所有参数打包成一个对象,变量的改下才能传递触发事件

export class TabItemParam {
  item?: string
  index: number = 0
  selectIndex: number = 0
}

 @Builder
  itemBuilder(item: TabItemParam ) {
  };

  @BuilderParam itemBuilderParam: (item: TabItemParam ) => void =
    this.itemBuilder;

...
 ForEach(this.tabDatas, (item: string, index: number) => {
            Column() {
              this.itemBuilderParam({ item: item, index: index, selectIndex: this.selectedIndex })
            }
            ...
          })
...

三、就是需要提供一个方法给外部去调用,触发滚动到指定的Tab。(不得不说,习惯了Android直接操作对象的方法的方式, 鸿蒙这种操作组件方法,实在有点绕。如果大家有其它更方便快捷的方式,可以评论分享下)

首先需要先定义一个Scroller,并且定义好一个给组件内部实现的方法A,一个给外部调用的方法B,并且 方法B内部是调用了方法A:

export class ZTabScroller {
  tabScroll?: (value: number) => void;

  scrollTo(index: number) {
    if (this.tabScroll) {
      this.tabScroll(index);
    }
  }
}

然后Tab在内部定义好scroller变量,并且在生命周期aboutToAppear()时定义好Scroller的方法A

  tabScroller?: TabScroller

  aboutToAppear(): void {
    if (this.tabScroller) {
      this.tabScroller.tabScroll = (index: number) => {
        this.itemScrollCenter(index)  //触发滚动到指定Tab的方法
      }
    }
  }


这样外部就可以创建TabScroller对象,赋值给Tab组件,并且可调用scrollTo方法从而触发Tab内部的itemScrollCenter(index)方法了

四:外部使用

搭配上Swiper组件, 并关联上selectedIndex就可以达到效果啦。

  tabsData: Array<string> = []
  @State selectedIndex: number = 0
  @State pagerIndex: number = 1
  tabScroller = new TabScroller()

  aboutToAppear(): void {
    this.tabsData.push('tab1')
    this.tabsData.push('tab2')
    this.tabsData.push('tab3')
  }

  build() {
   Column(){
     TabComponent({
       tabDatas: this.tabsData,
       selectedIndex: this.pagerIndex,
       tabScroller: this.tabScroller,
       itemBuilderParam: this.tabItemBuilder,
       isFixedCenter:false,
       onPageSelectChange: (index) => {
         this.pagerIndex = index
       }
     })


     Swiper() {
       this.swiperItem(`0`,Color.Green)
       this.swiperItem(`1`,Color.Red)
       this.swiperItem(`2`,Color.Orange)
     }
     .index(this.pagerIndex)
     .loop(false)
     .indicator(false)
     .onAnimationStart((index: number, targetIndex: number) => {
       this.tabScroller.scrollTo(targetIndex)
     })
     .padding({top:10})
     .height('100%')
     .width('100%')

   }
   .padding({top:20})
    .height('100%')
    .width('100%')
  }

 @Builder
  swiperItem(text:string,color:ResourceColor){
    Text(text)
      .width('100%')
      .height('100%')
      .backgroundColor(color)
      .textAlign(TextAlign.Center)
      .fontSize(30)

  }


 @Builder
  tabItemBuilder(item: TabItemData) {
    Column() {
      Text(item.title)
        .height(25)
        .fontSize(15)
        .textAlign(TextAlign.Center)
        .fontColor(item.index == item.selectIndex ? Color.Red : Color.Black)
        .margin({ left: 15, right: 15 })
        .animatableFontSize(item.index == item.selectIndex ? 20 : 15)
        .animation({ duration: 300, curve: Curve.Ease })

      if (item.index == item.selectIndex) {
        Blank()
          .backgroundColor(Color.Blue)
          .width(20)
          .height(3)
          .margin({ top: 5 })
          .borderRadius(2)
      }
    }
  };

//加上一个字体的选中和放大的动画效果
@AnimatableExtend(Text)
function animatableFontSize(fontSize: Length) {
  .fontSize(fontSize)
}

至此,就完成整个效果了。

完整代码:
https://github.com/Zeng-Ke/Tabs_Harmony

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

可视化大屏入口界面,炫酷科技又不失简洁时尚。

可视化大屏界面&#xff0c;大家见到很多了&#xff0c;当可视化大屏是多个系统的融合&#xff0c;而且彼此又相互独立&#xff0c;就需要设计一个入口页面&#xff0c;便于分流客户&#xff0c;这次我给大家分享一批。 设计可视化大屏入口界面时&#xff0c;可以结合炫酷科技…

#laravel部署安装报错loadFactoriesFrom是undefined method #

场景: 在git上clone一个项目代码吗laravel版本是5.6 php的版本是7.1 但是运行的时候一直提示错误 Call to undefined method Eachdemo\Rbac\RbacServiceProvider::loadFactoriesFrom() 解决办法: 给RbacServiceProvider&#xff0c;手动添加方…

中国生态地理区划更新和优化

在机器学习或深度学习研究时&#xff0c;建立的模型用于不同地区或时间的数据进行泛化时&#xff0c;其泛化能力往往较差&#xff0c;所以目前在遥感领域用深度学习或机器学习建模时很多文献都是建立分区的模型&#xff0c;即在不同的地理分区内建立模型&#xff0c;泛化时针对…

代码随想录算法训练营第十八天

力扣题部分: 530.二叉搜索树的最小绝对差 题目链接: 题面: 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 思路: 写关于二叉搜索树的问题&#xff0c;一定要先掌握二…

socket回显服务器练习

前言 什么是回显服务器(echo server)呢&#xff1f; 回显服务器接收客户端发送的任何数据&#xff0c;并将这些数据原封不动地发送回客户端。回显服务器在连接成功的基础上只需要知道如何在客户端将收到的信息打印输出到控制台即可。我接下来会使用两种方法来输出&#xff0c;…

“TCP粘包”不是TCP的问题!

前言 写RPC用了Netty。涉及到粘包拆包问题。想复习一下。发现网上博客多是概念模糊不清。没有触及本质或者没有讲清楚。 遂决定自己写一篇 “TCP粘包”是谁的问题&#xff1f; 首先我们要明确TCP是面向字节流的协议。也就是说我们在应用层想使用TCP来传输数据时&#xff0c;…

ARM 处理器异常处理机制详解

目录 异常 异常源 异常处理 异常向量表 安装设置异常向量表及保存现场指令 异常处理的返回 异常源与异常模式对应关系 异常响应优先级 ARM7-11 有7种基本工作模式&#xff0c;而 Cortex-A 系列处理器则额外支持 Monitor 模式&#xff1a; User&#xff1a;非特权模式&…

测试流程自动化实践!

测试流程自动化的最佳实践涉及多个方面&#xff0c;旨在提高测试效率、确保测试质量&#xff0c;并降低测试成本。以下是一些关键的实践方法&#xff1a; 1. 明确测试目标 确定测试范围&#xff1a;在开始自动化测试之前&#xff0c;需要明确哪些功能、模块或场景需要被测试。…

Leetcode JAVA刷刷站(39)组合总和

一、题目概述 二、思路方向 为了解决这个问题&#xff0c;我们可以使用回溯算法来找到所有可能的组合&#xff0c;使得组合中的数字之和等于目标数 target。因为数组中的元素可以无限制地重复选择&#xff0c;所以在回溯过程中&#xff0c;我们不需要跳过已经选择的元素&#x…

python爬虫爬取某图书网页实例

文章目录 导入相应的库正确地设置代码的基础部分设置循环遍历遍历URL保存图片和文档全部代码即详细注释 下面是通过requests库来对ajax页面进行爬取的案例&#xff0c;与正常页面不同&#xff0c;这里我们获取url的方式也会不同&#xff0c;这里我们通过爬取一个简单的ajax小说…

第N6周:中文文本分类-Pytorch实现

本文为365天深度学习训练营 中的学习记录博客原作者&#xff1a;K同学啊 一、准备工作 任务说明 本次将使用PyTorch实现中文文本分类。主要代码与N1周基本一致&#xff0c;不同的是本次任务中使用了本地的中文数据&#xff0c;数据示例如下&#xff1a; 任务&#xff1a; ●1…

Diffusion Model相关论文解析之(二)DENOISING DIFFUSION IMPLICIT MODELS

目录 1、摘要2、创新点3、主要公式4、自己的理解&#xff0c;对错不确定 1、摘要 ‌Denoising Diffusion Implicit Models (DDIM)‌是一种扩散模型的改进版本&#xff0c;旨在加速采样过程并提高采样速度。DDIM通过引入非马尔可夫扩散过程&#xff0c;相对于传统的去噪扩散概率…

H. Ksyusha and the Loaded Set

https://codeforces.com/contest/2000/problem/H div3 H 一开始看就感觉要维护一些比较有趣的量 看了一下数据范围ai<2e6,k<2e6 似乎可以直接开一个线段树来表示是否存在集合当中 我们开4e6维护每个数字是否存在&#xff0c;ai2e6时候k2e6&#xff0c;最大是4e6 存在…

用python 实现一个简易的“我的世界”游戏(超详细教程)

编写一个完整的“我的世界”游戏风格的程序在Python中是一个相当复杂的任务&#xff0c;因为它涉及到图形渲染、物理引擎、用户交互等多个方面。然而&#xff0c;我们可以创建一个简化的、基于文本的“我的世界”风格的探险游戏&#xff0c;来展示基本的游戏逻辑和交互。 第一步…

基于Spring Boot的高效宠物购物平台

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

STM32单片机 主、从、触发模式

主模式&#xff0c;将定时器内部信号映射到TRGO引脚&#xff0c;用于触发别的外设 在手册 控制寄存器2 一节可以知道各种主模式的解释从模式&#xff0c;接收其他外设或者自身外设的一些信号&#xff0c;用于控制自身定时器的运行&#xff0c;被别的信号控制触发源选择&#xf…

使用Logstash同步MySql数据到Elasticsearch

1、下载Logstash logstash下载地址 环境为 windows 2、将Logstash压缩包进行解压 将 mysql 驱动文件放在文件夹内 在Logstash根目录下创建 mysql-es.conf文件 input {jdbc {jdbc_driver_library > "E:\linshi\mysql-connector-java-8.0.11\mysql-connector-java-8.…

医院器械管理系统的设计与开发(全网独一无二,24年最新定做)

目录 文章目录: 前言&#xff1a; 系统功能&#xff1a; 1.用户 2.管理员 系统详细实现界面&#xff1a; 参考代码&#xff1a; 为什么选择我&#xff1a; 前言&#xff1a; 博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全…

数据库基础增删改练习

1.student学生表中&#xff0c;字段有姓名name&#xff0c;年龄age&#xff0c;要求查询姓张&#xff0c;并且年龄在18到25岁之间的学生 2.查询article文章表中&#xff0c;发表日期create_date在2019年1月1日上午10点30分至2019年11月10日下午4点2分的文章 3.查询article文章表…

Deepin-获取屏幕缩放比例

Deepin-获取屏幕缩放比例 一、概述二、实现代码 一、概述 环境&#xff1a;UOS、Deepin 我的目的是为了获取屏幕的缩放比例值&#xff0c;就是获取如下的值 我们可以去读取当前的环境变量值&#xff0c;在Qt Creator中可以看到这个值 二、实现代码 相关的Qt接口如下&…