鸿蒙Next如何自定义标签页
前言
项目需求是展示标签,标签的个数不定,一行展示不行就自行换行。但是,使用鸿蒙原生的 Grid 后发现特别的难看。然后就想着自定义控件。找了官方文档,发现2个重要的实现方法,但是,官方的demo中讲的很少,需要自己去看去思考。
效果图如下:

注意点:
- 需要计算整体布局的宽高,这和 Android 差不多。
- 注意 margin 的计算
具体的代码如下:
/*** 自定义标签页面*/
@Component struct CustomTagView {screenWidth: number = 0aboutToAppear(): void {let dis = display.getDefaultDisplaySync();let width = dis.widthlet height = dis.height// width 是单位是 px, 转换为 vp ,因为 onMeasureSize 和 onPlaceChildren 得到的 width margin 以及 padding 都是 vplet w = px2vp(width)let h = px2vp(height)this.screenWidth = wconsole.log("TagView aboutToAppear width = " + width + " , height = " + height + ", w = " + w + ", h = " + h)}@Builder childBuilder() {}@BuilderParam buildTagView: () => void = this.childBuilderresult: SizeResult = {width: 0,height: 0}// 第一步:计算各子组件的实际大小以及设置布局本身的大小onMeasureSize(selfLayoutInfo: GeometryInfo, children: Array<Measurable>, constraint: ConstraintSizeOptions) {let parentWidth = selfLayoutInfo.widthlet parentHeight = selfLayoutInfo.heightconsole.log("TagView onMeasureSize parentWidth = " + parentWidth + " , parentHeight = " + parentHeight)let startPosX = 0let columNumber = 1let childHeight = 0children.forEach((child) => {// 得到子控件的实际大小let result: MeasureResult = child.measure({minHeight: constraint.minHeight,minWidth: constraint.minWidth,maxWidth: constraint.maxWidth,maxHeight: constraint.maxHeight})let padding = child.getPadding()let border = child.getBorderWidth()console.log("TagView onMeasureSize = child width = " + result.width + " , height = " + result.height+ " , padding = [" + padding.start + ", " + padding.end + ", " + padding.top + ", " + padding.bottom +"], border = ["+ border.start + ", " + border.end + ", " + border.top + ", " + border.bottom + "]")/// 计算 布局所需的高度, 宽度默认为屏幕的宽度childHeight = result.height + child.getMargin().topstartPosX += result.width + child.getMargin().startif (startPosX > parentWidth) {columNumber++startPosX = result.width + child.getMargin().start}})// 父布局的宽和高,即承载 child 布局的宽和高,这里指的就是 TagView 的宽和高this.result.width = this.screenWidth;this.result.height = childHeight * columNumber + 10 // 加10是为了底部多点空间return this.result;}// 第二步:放置各子组件的位置onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Array<Layoutable>, constraint: ConstraintSizeOptions) {let startPosX = 0let startPosY = 0let posY = 0let parentWidth = selfLayoutInfo.widthconsole.log("TagView onPlaceChildren parentWidth = " + selfLayoutInfo.width + " , parentHeight = " + selfLayoutInfo.height)children.forEach((child) => {startPosX += child.getMargin().start// 如果一行的控件的长度大于屏幕宽度则换行if (startPosX + child.measureResult.width > parentWidth) {startPosY += child.measureResult.height + child.getMargin().topstartPosX = child.getMargin().start}posY = startPosYconsole.log("TagView child width = " + child.measureResult.width + " , height = " + child.measureResult.height +" , margin left = " + child.getMargin().start)child.layout({ x: startPosX, y: posY })startPosX += child.measureResult.width})}build() {this.buildTagView()}
}
调用的方法也很简单,下面是个调用的demo
@Entry
@Component
struct TestPage {@State name: string = 'hello'// @Provide 参数 key 必须是 string@Provide('provideName') pName: string = "哈哈"@Provide('count') count: number = 4textWidth: number = 0aboutToAppear(): void {let width = MeasureText.measureText({ textContent: '返厂无忧券1', fontSize: '13vp' });let w = px2vp(width)console.log("TestPage aboutToAppear >>>> width = " + width + " , w = " + w)// 如果有换行,那么长度等于最长的一行let textSize = MeasureText.measureTextSize({ textContent: '返厂无忧券1\n返厂无忧券1234', fontSize: '13vp' })let w2 = textSize.widthlet h2 = textSize.heightthis.textWidth = px2vp(w2 as number)console.log("TestPage aboutToAppear >>>> w2 = " + w2 + " , h2 = " + h2)}// @Build 参数按值传递,状态变量改变不会引起 @Build 方法内的 UI 刷新// 但是,Text(" ----- " + this.name) 中的 UI 会刷新@BuildernameView(name: string) {Text(name)}// @Build 参数按引用传递的话,状态变量(@State name) 改变,@Build 方法内的 UI 会刷新@BuildernameView2(tmp: Tmp) {Text('V2 : name = ' + tmp.params)}// 通过builder的方式传递多个组件,作为自定义组件的一级子组件(即不包含容器组件,如Column)@BuilderTagViewChildren() {ForEach(['你好,哪吒2', '大圣归来啊', '我是名字超长的但是很厉害', 'hello world', '你真的好厉害啊哈哈', '没空看', '非凡','再来一个很长的名字啊', 'OK'],(data: string, index: number) => { // 暂不支持lazyForEach的写法Text(data).fontSize(13).fontColor(Color.Red).margin({ left: 3, top: 4 })// .width(100)// .height(100).borderWidth(1).border({ radius: 4 }).padding(5).textAlign(TextAlign.Center)// .offset({ x: 10, y: 20 })})}build() {Column() {Column() {CustomTagView({ buildTagView: this.TagViewChildren })}.backgroundColor(Color.Pink).margin({ top: 2 })}.width('100%').height('100%').alignItems(HorizontalAlign.Start)}
}
好了,具体的可以参考下 demo 啦,有疑问的可以一起交流。
相关文章:
鸿蒙Next如何自定义标签页
前言 项目需求是展示标签,标签的个数不定,一行展示不行就自行换行。但是,使用鸿蒙原生的 Grid 后发现特别的难看。然后就想着自定义控件。找了官方文档,发现2个重要的实现方法,但是,官方的demo中讲的很少&…...
知识拓展:Python 接口实现方式对比:Protocol vs @implementer
Python 接口实现方式对比:Protocol vs implementer 1. 两种接口实现方式 1.1 Python Protocol(结构化子类型) from typing import Protocolclass DownloadHandlerProtocol(Protocol):def download_request(self, request: Request, spider:…...
开源程序wordpress在海外品牌推广中的重要作用
WordPress作为全球最流行的开源内容管理系统(CMS),在全球网站搭建中占据超过40%的市场份额。其强大的功能、灵活性和易用性使其成为企业进行海外品牌推广的首选平台。以下是WordPress在海外品牌推广中的重要性分析: 1. 多语言支持与本地化 WordPress通…...
【Python爬虫(89)】爬虫“反水”:助力数字版权保护的逆向之旅
【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取ÿ…...
k8s面试题总结(五)
1.考虑一种情况,即公司希望通过维持最低成本来提高其效率和技术运营速度。您认为公司将如何实现这一目标? 公司可以通过构建 CI/CD 管道来实现 DevOps 方法,但是这里可能出现的一个问题是配置可能需要一段时间才能启动并运行。 因此&#x…...
文章精读篇——用于遥感小样本语义分割的可学习Prompt
题目:Learnable Prompt for Few-Shot Semantic Segmentation in Remote Sensing Domain 会议:CVPR 2024 Workshop 论文:10.48550/arXiv.2404.10307 相关竞赛:https://codalab.lisn.upsaclay.fr/competitions/17568 年份&#…...
Spring Boot2.0之十 使用自定义注解、Json序列化器实现自动转换字典类型字段
前言 项目中经常需要后端将字典类型字段值的中文名称返回给前端。通过sql中关联字典表或者自定义函数不仅影响性能还不能使用mybatisplus自带的查询方法,所以推荐使用自定义注解、Json序列化器,Spring的缓存功能实现自动转换字典类型字段。以下实现Spri…...
从电子管到量子计算:计算机技术的未来趋势
计算机发展的历史 自古以来人类就在不断地发明和改进计算工具,从结绳计数到算盘,计算尺,手摇计算机,直到1946年第一台电子计算机诞生,虽然电子计算机至今虽然只有短短的半个多世纪,但取得了惊人的发展吗,已经经历了五代的变革。计算机的发展和电子技术的发展密切相关,…...
将CUBE或3DL LUT转换为PNG图像
概述 在大部分情况下,LUT 文件通常为 CUBE 或 3DL 格式。但是我们在 OpenGL Shader 中使用的LUT,通常是图像格式的 LUT 文件。下面,我将教大家如何将这些文件转换为 PNG 图像格式。 条形LUT在线转换(不是8x8网络)&am…...
python文件的基本操作,文件读写
1.文件 1.1文件就是存储在某种长期存储设备上的一段数据 1.2文件操作 打开文件-->读写文件-->关闭文件 注意:可以只打开和关闭文件不进行任何操作 1.3文件对象的方法 1.open():创建一个file对象,默认以只读模式打开 2.read(n):n表示从文件中…...
华为认证考试证书下载步骤(纸质+电子版)
华为考试证书可以通过官方渠道下载相应的电子证书,部分高级认证如HCIE还支持申请纸质证书。 一、华为电子版证书申请步骤如下: ①访问华为培训与认证网站 打开浏览器,登录华为培训与认证官方网站 ②登录个人账号 在网站首页,点…...
正式页面开发-登录注册页面
整体路由设计: 登录和注册的切换是切换组件或者是切换内容(v-if和 v-else),因为点击两个之间路径是没有变化的。也就是登录和注册共用同一个路由。登录是独立的一级路由。登录之后进到首页,有三个大模块:文章分类&…...
nss刷题5(misc)
[HUBUCTF 2022 新生赛]最简单的misc 打开后是一张图片,没有其他东西,分离不出来,看看lsb,红绿蓝都是0,看到头是png,重新保存为png,得到一张二维码 扫码得到flag [羊城杯 2021]签到题 是个动图…...
深入Linux序列:进程的终止与等待
在之前的学习中,我们知道我们的进程在运行结束的时候,那么它并不会立即进入死亡状态,而是先进入僵尸状态,维持僵尸状态一段时间,那么此时在僵尸状态中的进程,那么它的内核数据已经移出内存被清理了…...
蓝桥杯之日期问题2
文章目录 需求11.1 代码 2.需求22.1代码 需求1 2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2 日。因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202,恰好是一个回文数。我们称这样的日期是回文日期…...
【STL】7.STL常用算法(1)
STL常用算法(1) 前言简介一.遍历算法1.for_each2.transform 二.查找算法1.find2.find_if3.adjacent_find4.binary_search5.count6.cout_if 三.排序算法1.sort2.random_shuffle3.merge4.reverse 总结 前言 stl系列主要讲述有关stl的文章,使用S…...
uniapp 本地数据库多端适配实例(根据运行环境自动选择适配器)
项目有个需求,需要生成app和小程序,app支持离线数据库,如果当前没有网络提醒用户开启离线模式,所以就随便搞了下,具体的思路就是: 一个接口和多个实现类(类似后端的模板设计模式)&am…...
百度觉醒,李彦宏渴望光荣
文 | 大力财经 作者 | 魏力 2025年刚刚开年,被一家名为DeepSeek的初创公司强势改写。在量化交易出身的创始人梁文锋的带领下,这支团队以不到ChatGPT 6%的训练成本,成功推出了性能可与OpenAI媲美的开源大模型。 此成果一经问世,…...
【算法工程】大模型局限性新发现之解决能连github但无法clone项目的问题
最近,linux服务器遇到一个奇怪的问题,能ping通github,但是无法clone git项目,尝试了各种大模型,都提到代理啥的问题,发现没有一个能解决问题。 后来尝试设置 http.sslVerify 为 false,才解决问题…...
SOME/IP-SD -- 协议英文原文讲解3
前言 SOME/IP协议越来越多的用于汽车电子行业中,关于协议详细完全的中文资料却没有,所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块: 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 5.1.2.4…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
