当前位置: 首页 > news >正文

鸿蒙新特性——Badge 徽章组件详解

一、引言

在移动应用界面中,角标/徽章(Badge)是一个小但无处不在的 UI 元素。社交 App 的消息列表上那个红色的未读数字、购物 App 底部 Tab 栏上购物车的商品数量角标、邮件客户端的收件箱未读计数、系统通知栏的应用角标——它们都以一个微小的圆形贴片附着在图标或列表项上,用最简洁的方式传递"这里有新的内容"的信息。

虽然角标看起来只是一个"带数字的小圆点",但在传统开发中实现它并非易事。你需要计算角标相对于宿主元素的位置(通常是右上角),处理数字过长时的显示策略(超过 99 显示"99+“还是”…"),管理空状态的显示逻辑(未读数为 0 时不显示角标),以及确保角标在不同屏幕密度下的大小一致。如果每个需要角标的场景都手写这些逻辑,代码会迅速膨胀。

而 HarmonyOS 提供了Badge组件——一个专用于徽章/角标的容器组件。它包裹任意子组件,自动在指定位置渲染一个带有数字或文本的角标。支持自定义位置(右上/右下/右侧)、颜色、大小、最大显示数字(超出以"99+"展示),并且当数值为 0 或空字符串时角标自动隐藏。

本文通过一个"消息中心"Demo 深入讲解 Badge 组件的核心用法:角标的位置如何设置?颜色和大小如何定制?如何与列表联动实现"标为已读"清除角标?以及 Badge 在实际业务中的最佳实践。

阅读完本文,你将能够:

  • 使用 Badge 组件为任意 UI 元素添加角标
  • 掌握 Badge 的位置(BadgePosition)和样式配置
  • 实现消息列表的未读角标与交互联动
  • 理解 Badge 的显示/隐藏逻辑和 maxCount 截断策略
  • 构建完整的消息中心页面

二、Badge 组件 API 总览

2.1 构造函数

Badge(options:BadgeOptions)
interfaceBadgeOptions{value:string;// 角标显示的文本内容position?:BadgePosition;// 角标位置,默认 RightTopstyle?:BadgeStyle;// 角标样式配置maxCount?:number;// 最大显示数字,默认 99}
参数类型默认值说明
valuestring必填角标内容,"0"或空字符串表示不显示角标
positionBadgePositionRightTop角标相对宿主元素的方位
styleBadgeStyle默认样式角标的颜色和大小配置
maxCountnumber99数字超过此值时显示"99+"

2.2 BadgePosition 枚举

enumBadgePosition{Right,// 右侧居中RightTop,// 右上角(默认,最常用)Left// 左侧居中}
位置视觉效果适用场景
RightTop角标在宿主右上角,略微溢出消息列表头像、Tab 图标角标
Right角标在宿主右侧居中列表项尾部状态标记
Left角标在宿主左侧居中特殊布局需求

绝大多数场景使用默认的RightTop,它符合用户"角标在右上角"的认知习惯。RightLeft适用于非标准的布局需求。

2.3 BadgeStyle 对象

interfaceBadgeStyle{badgeColor?:ResourceColor;// 角标背景颜色,默认红色badgeSize?:number|string;// 角标大小,默认 12vp}
属性类型默认值说明
badgeColorResourceColor#FA2A2D(红色)角标圆形的背景色
badgeSizenumber/string16vp角标圆形的直径

badgeSize控制角标圆形的直径。对于头像上的角标,建议 14-18vp;对于图标上的角标,建议 12-16vp。角标内的文字大小会根据badgeSize自动缩放。

2.4 value 参数的特殊逻辑

value是 Badge 最核心且微妙的参数。它的取值直接决定了角标的显示行为:

value 值显示效果
"5"显示数字 5
"99"显示数字 99
"123"如果 maxCount=99,显示"99+"
"0"不显示角标
""不显示角标
"New"显示文本"New"
"!"显示感叹号标点

关键逻辑:当value"0"或空字符串""时,Badge 会自动隐藏角标。这一设计让未读计数为 0 时无需额外条件判断即可自动隐藏角标,大大简化了业务代码。

2.5 Badge 作为容器组件

Badge 是一个容器组件——它包裹(sling)一个子组件,角标附着在这个子组件之上:

Badge({value:'5',position:BadgePosition.RightTop}){// 任意子组件——图标、头像、文本等Image($r('app.media.icon')).width(48).height(48)}

这意味着 Badge 不改变子组件的固有布局和尺寸,它只是在上方叠加了一个角标。子组件可以是任何 ArkUI 组件:ImageTextRowColumn,甚至嵌套的复杂组件。

三、Demo 设计:消息中心

3.1 功能概述

Demo 是一个"消息中心",模拟即时通讯应用的消息列表页面:

  1. 未读总览卡片:顶部蓝色卡片显示总未读数(带 Badge),以及"全部已读"按钮
  2. 消息列表:8 条模拟消息,每条显示头像(带未读角标)、联系人名、预览、时间
  3. 点击标为已读:点击消息项清除该条角标,总计数同步刷新
  4. 徽章配置:切换角标位置(右上/右下/右侧)、切换角标颜色(红/蓝/绿/橙)

3.2 交互点

#交互说明
1点击消息项将该条消息标为已读,角标消失,总计数 -N
2全部已读一键清除所有未读角标,总计数归零
3角标位置切换右上 / 右下 / 右侧三种位置实时切换
4角标颜色切换红 / 蓝 / 绿 / 橙四种颜色实时切换

四、完整代码实现

4.1 数据模型与状态

interfaceMessageItem{id:number;name:string;avatar:string;preview:string;time:string;unread:number;}@Statemessages:MessageItem[]=[{id:1,name:'张小明',avatar:'#1677FF',preview:'明天的会议改到下午3点可以吗?',time:'刚刚',unread:5},{id:2,name:'李设计',avatar:'#FF4D4F',preview:'新的设计稿已经上传到Figma了',time:'5分钟前',unread:0},{id:3,name:'王开发',avatar:'#52C41A',preview:'这个bug我修好了,你测一下',time:'10分钟前',unread:99},{id:4,name:'赵产品',avatar:'#FF9800',preview:'PRD已经更新了,加了一个新需求',time:'30分钟前',unread:2},// ... 更多消息项];@StatetotalUnread:number=0;@StatebadgePosition:BadgePosition=BadgePosition.RightTop;@StatebadgeColor:string='#FF4D4F';

每条消息的unread字段决定角标显示的数值。totalUnread是所有消息未读数的总和,显示在顶部总览卡片中作为总角标。

4.2 角标值计算

getBadgeValue(count:number):string{if(count===0){return'';}if(count>this.badgeMaxCount){returnthis.badgeMaxCount.toString().concat('+');}returncount.toString();}

count为 0 时返回空字符串""——根据 Badge 的 value 逻辑,空字符串会使角标自动隐藏。当count超过maxCount(默认 99)时返回"99+",否则返回数字的字符串形式。

4.3 标为已读的实现

markAsRead(id:number):void{constnewList:MessageItem[]=[];for(leti=0;i<this.messages.length;i++){if(this.messages[i].id===id){newList.push({id:this.messages[i].id,name:this.messages[i].name,avatar:this.messages[i].avatar,preview:this.messages[i].preview,time:this.messages[i].time,unread:0// 清零});}else{newList.push(this.messages[i]);}}this.messages=newList;this.updateTotal();}

由于 ArkTS 的@State要求不可变更新,不能直接修改数组元素,而是需要创建新数组。markAsRead遍历消息列表,将匹配 ID 的消息项unread设为 0(替换为新对象),其余项保持不变。最后将新数组赋值给this.messages触发 UI 刷新。

updateTotal()遍历所有消息累加unread,更新totalUnread状态——顶部总览卡片的总角标随之刷新。

4.4 消息列表项

ForEach(this.messages,(msg:MessageItem)=>{Column(){Row(){Badge({value:this.getBadgeValue(msg.unread),position:this.badgePosition,style:{badgeSize:16,badgeColor:this.badgeColor}}){Row().width(44).height(44).borderRadius(22).backgroundColor(msg.avatar).justifyContent(FlexAlign.Center)}Column(){Row(){Text(msg.name).fontSize(15).fontColor('#1a1a2e').fontWeight(FontWeight.Medium).layoutWeight(1)Text(msg.time).fontSize(11).fontColor('#BBBBCC')}Text(msg.preview).fontSize(13).fontColor('#9999AA').maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis})}.layoutWeight(1).margin({left:12})}.width('100%').padding({top:14,bottom:14,left:4,right:4}).onClick(()=>{this.markAsRead(msg.id);})}.width('100%').border({width:{bottom:0.5},color:'#F2F3F5'})})

每个消息项是一个水平布局:Badge 包裹彩色圆形头像 → 联系人名 + 消息预览。点击整行触发markAsRead,该条消息的未读角标消失。

Badge 的positionstyle.badgeColor都绑定到@State变量,用户在配置面板切换时所有消息项的角标同步更新。

4.5 总览卡片与 Badge

Row(){Badge({value:this.getBadgeValue(this.totalUnread),position:BadgePosition.RightTop,style:{badgeSize:18,badgeColor:'#FF4D4F'}}){Image($r('sys.symbol.message')).width(28).height(28).fillColor('#FFFFFF')}.margin({right:12})Column(){Text('未读消息'.concat(' ',this.totalUnread.toString(),' 条')).fontSize(16).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)Text('点击消息项即可标为已读').fontSize(12).fontColor('#FFFFFF88')}// ...Text('全部已读').fontSize(12).fontColor('#FFFFFF').onClick(()=>{this.markAllRead();})}

顶部卡片使用系统符号sys.symbol.message作为图标,用 Badge 包裹显示总未读数。getBadgeValue(this.totalUnread)在总数为 0 时返回空字符串,角标自动消失。"全部已读"按钮调用markAllRead()将所有消息的unread设为 0。

4.6 角标位置和颜色配置

// 位置切换ForEach(['右上','右下','右侧'],(pos:string)=>{Text(pos).onClick(()=>{if(pos==='右上'){this.badgePosition=BadgePosition.RightTop;}if(pos==='右下'){this.badgePosition=BadgePosition.Right;}if(pos==='右侧'){this.badgePosition=BadgePosition.Right;}})})// 颜色切换ForEach(['#FF4D4F','#1677FF','#52C41A','#FF9800'],(c:string)=>{Row().width(24).height(24).borderRadius(12).backgroundColor(c).border({width:this.badgeColor===c?3:0,color:c}).onClick(()=>{this.badgeColor=c;})})

颜色配置以圆形色块的方式展示,选中态通过加粗边框(border.width: 3)表示。这种"颜色选择器"的设计既直观又节省空间。

五、关键技术点详解

5.1 Badge 的位置与溢出策略

Badge 的角标相对于宿主元素的定位方式为:角标圆心位于宿主元素的指定方位边缘。以RightTop为例,角标的圆心在宿主元素的右上角顶点附近,由于角标是一个圆形,它会部分溢出到宿主元素范围之外——这正是我们期望的"角标贴在上方"的效果。

角标的溢出量等于角标的半径(badgeSize / 2)。例如badgeSize: 16时,角标会在右上角向外溢出约 8vp。这意味着在布局时需要为 Badge 宿主元素留出适当的 margin 或 padding,避免角标被父容器裁剪。

// 推荐:为 Badge 宿主留出角标溢出空间.margin({top:4,right:4})

5.2 value 空值逻辑的妙用

Badge 最巧妙的设计是value为空字符串或"0"时自动隐藏角标。这一特性在业务代码中非常实用——开发者不需要在每次使用 Badge 时都加一层条件判断:

// 不需要这样写(Badge 自动处理了零值隐藏)if(unreadCount>0){Badge({value:unreadCount.toString()}){...}}else{// 仅显示宿主元素,无角标}// 直接这样写即可Badge({value:this.getBadgeValue(unreadCount)}){...}

getBadgeValue方法返回空字符串(未读数为 0)、“99+”(超过 99)或数字字符串——Badge 内部自动处理了这三种情况的显示逻辑。

5.3 Badge 的"仅圆点"模式

在某些场景下(如"有新的系统通知但不需要显示具体数量"),需要一个不带数字的红色小圆点。可以通过设置value: ' '(一个空格)或利用小到不可见的文字来实现,但更规范的做法是设置一个极小的值。不过,ArkUI 的 Badge 组件不原生支持"dot-only"模式。变通方案是设置value: ''时角标隐藏,而需要圆点时设置一个不可见字符(如零宽空格)。

对于大多数业务场景,"0 不显示、>0 显示数字"的方案已经足够。

5.4 @State 数组的不可变更新

Demo 中的markAsRead方法展示了 ArkTS 中@State数组的标准更新模式——不可变替换:

// 错误做法(直接修改,ArkTS 编译器会报错或 UI 不刷新)this.messages[idx].unread=0;// 正确做法(创建新数组和新对象)constnewList:MessageItem[]=[];for(leti=0;i<this.messages.length;i++){if(this.messages[i].id===id){newList.push({/* 展开字段, unread: 0 */});}else{newList.push(this.messages[i]);}}this.messages=newList;

这种模式在 ArkTS 中非常常见:遍历 → 判断 → 创建新对象 → 推入新数组 → 赋值给@State变量。虽然代码量比命令式修改多一些,但它保证了 UI 的可靠刷新和状态的不可变性。

5.5 Badge 与系统符号的组合使用

Demo 的顶部卡片使用了系统符号(System Symbol)sys.symbol.message作为图标:

Image($r('sys.symbol.message')).width(28).height(28).fillColor('#FFFFFF')

系统符号是一组内置的 SF Symbol 风格图标,通过$r('sys.symbol.xxx')引用。它们不需要额外引入资源文件,且颜色通过.fillColor()控制。Badge 包裹这个图标后,角标自动出现在图标的右上角。

六、运行效果

6.1 初始状态

进入"消息中心"页面,顶部蓝色卡片展示总未读角标(包裹消息图标)+ “未读消息 110 条”(5+0+99+2+0+1+0+3=110)。下方 8 条消息,每条显示彩色圆形头像(带红色未读角标)、联系人名、消息预览和时间。

张小明显示角标"5",王开发显示角标"99+ "(因 unread=99 达到 maxCount 阈值),李设计和项目群的未读为 0,角标不显示。

6.2 标为已读

点击王开发的消息行 → 该行角标消失(unread 从 99 变为 0),顶部总角标从 110 变为 11。再点击张小明 → 角标消失,总计数从 11 变为 6。

6.3 一键全部已读

点击"全部已读"按钮 → 所有消息项的角标消失,顶部总角标自动隐藏(totalUnread归零,getBadgeValue(0)返回空字符串)。

6.4 角标配置调整

在配置面板点击"右下" → 所有消息项的角标位置从右上角变为右下角居中。点击绿色色块 → 所有角标颜色从红色变为绿色#52C41A。切换回"右上" + 红色,恢复正常外观。

七、总结

本文通过一个"消息中心"实战 Demo,深入讲解了 HarmonyOS Badge 徽章组件的核心用法:

  1. Badge 作为容器组件:包裹任意子组件,在指定位置渲染角标
  2. BadgePosition:RightTop(右上,默认)/ Right(右侧居中)/ Left(左侧居中)三种位置
  3. BadgeStylebadgeColor(背景色)和badgeSize(直径)控制角标外观
  4. value 空值隐藏"""0"时角标自动不显示,简化零值判断代码
  5. maxCount 截断:数字超过 maxCount 显示"99+",避免角标过宽
  6. 与列表联动:通过@State数组不可变更新实现"标为已读→角标消失"的交互闭环

Badge 组件虽然 API 简洁,但在实际应用中承载了大量微妙的 UI 逻辑——位置计算、数字截断、零值隐藏——这些逻辑如果由开发者手写,既容易出错又占用精力。Badge 将这些细节封装进组件内部,让开发者专注于业务逻辑而非角标渲染。这正是 ArkUI"组件化封装"设计哲学的体现。

从消息列表到购物车角标,从邮件未读到通知提醒,Badge 以最简洁的方式传递"这里有新内容"的信息。希望本文能帮助你在实际项目中高效运用 Badge 组件。


本文基于 HarmonyOS NEXT API 24 编写,代码经 DevEco Studio 6.1.1 编译验证通过。

http://www.cnnetsun.cn/news/3148648.html

相关文章:

  • Linux 用户管理知识与应用实践(二:用户相关命令与示例)
  • 高速 ADC 与 FPGA LVDS 接口设计:5 项 PCB 布线规则与 IDELAY 时序校准实战
  • 远控横评:向日葵、ToDesk、UU 远程,远程玩游戏差距有多大
  • Transformers自动化训练全流程优化实战
  • 助睿实验7-3:可视化探索
  • 基于51单片机的教室智能照明灯控制系统光控人数检测定做定制电子13(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • kotlin-basic-blog
  • 89个公共Tracker如何让BT下载告别“孤岛困境“?
  • 剧云推出分镜大师:让剧本更快变成可拍摄的镜头方案
  • Deceive:终极游戏隐身指南 - 如何在英雄联盟、VALORANT和符文大地传说中保持隐身状态
  • 《鸿蒙原生应用从0-1构建:项目工程结构与核心配置全景解析》
  • ExplorerPatcher深度解析:重塑Windows界面体验的高效工具
  • Node.js 插件沙箱:开放扩展之前先限制能力
  • Go 泛型的运行时性能:单态化、接口装箱与编译器优化的基准分析
  • OBS美颜文章_终极指南
  • 别再手写Bug了!用Python+LangGraph实现AI自修复代码的完整指南
  • AI机器学习高级数学与优化
  • SSTI攻击链构造手册(带WAF绕过)
  • 创客指南:oDrive X2212电机从零到闭环的完整配置流程
  • 2026外贸获客渠道全面洗牌:AI正在重新分配全球流量,你的品牌在答案里吗?
  • 香农公式极限推导
  • R语言多分类Logistic回归变量筛选实战:最优子集与逐步回归
  • 【硬件+APP+云平台】9.智能洗衣系统-WiFi-基于STM32嵌入式物联网单片机软硬件毕业生系统设计
  • 2026免费好用的去水印软件推荐:电脑手机在线工具优缺点对比
  • 题解:洛谷 B4554 [GESP202606 二级] 菱形
  • 基于EGEUNet的烟叶病害智能识别系统设计与实现
  • 如何免费下载国家中小学智慧教育平台电子课本PDF:完整指南
  • LSTM 超参数网格搜索:记忆单元、批次大小与 Dropout 的 3 维对比实验
  • Java毕业设计-基于 JavaWeb 的美容美发管理系统的设计与实现 美容院会员消费预约管理系统(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • 国产大模型生存四道生死线:成本、适配、进化与变现