HarmonyOS7 消息徽标展示页实战:Badge 不只是会用,还要用得顺手
文章目录
- Badge 不是贴上去就完事
- 清除徽标要清完整状态
- 三种徽标场景怎么选
- 页面结构怎么看
- 完整代码
- 写在最后
Badge 最常见的误用,是把它当成一个红点装饰。实际做消息页、应用入口、Tab 栏时,它表达的是“还有东西没处理”。这个状态如果和真实数据脱节,用户点进去发现什么都没有,体验会很糟。
这篇用 HarmonyOS7 的 Badge 做了三个场景:应用图标未读数、徽标位置对比、Tab 栏消息提醒。它不复杂,但很适合拿来练状态同步。
Badge 不是贴上去就完事
页面里定义了AppInfo和TabItem。前者记录应用图标、背景色、未读数、更新状态和圆点状态;后者负责 Tab 栏里的标题、图标和徽标数字。
这种数据结构比直接在 UI 里写死数字好很多。未读数来自接口也好,来自本地缓存也好,最后都能落到同一个字段上,Badge 只负责展示。
清除徽标要清完整状态
clearBadge()是这篇的关键。点击某个应用后,它会把对应应用的msgCount、hasUpdate、hasDot一起清掉,而不是只把数字改成 0。
clearBadge(appId:number){this.apps=this.apps.map((app:AppInfo)=>{if(app.id===appId){return{id:app.id,name:app.name,icon:app.icon,bgColor:app.bgColor,msgCount:0,hasUpdate:false,hasDot:false}}returnapp})}这点很贴近真实业务。一个入口可能同时有未读消息、版本更新、小红点提醒,如果只清其中一个,页面就会出现“数字没了但红点还在”的尴尬状态。
三种徽标场景怎么选
数字徽标适合明确数量,比如未读消息、待处理订单;圆点徽标适合弱提醒,比如有新内容但数量不重要;Tab 徽标适合告诉用户某个栏目里有待处理内容。
别所有地方都用数字。超过一定数量后,用户其实不关心精确值,显示99+或一个红点往往更清爽。
页面结构怎么看
BadgeMessagePage主要看两块状态:apps控制应用图标区,tabs和selectedTab控制底部 Tab。读代码时可以先看数据数组,再看 Badge 包裹了哪个组件,最后看点击事件如何更新状态。
如果你要把这个案例放进项目,我建议把清除逻辑接到真实阅读状态上。Badge 的展示可以很轻,但背后的状态一定要准。
完整代码
下面是完整代码。入口组件名是BadgeMessagePage,可以直接作为消息入口或 Tab 徽标的参考实现。
// Badge 徽标组件全解析interfaceAppInfo{id:numbername:stringicon:stringbgColor:stringmsgCount:numberhasUpdate:booleanhasDot:boolean}interfaceTabItem{label:stringicon:stringbadgeCount:number}@Entry@Componentstruct BadgeMessagePage{@Stateapps:AppInfo[]=[{id:1,name:'消息',icon:'💬',bgColor:'#2563EB',msgCount:99,hasUpdate:false,hasDot:false},{id:2,name:'邮件',icon:'📧',bgColor:'#DC2626',msgCount:3,hasUpdate:false,hasDot:false},{id:3,name:'通知',icon:'🔔',bgColor:'#D97706',msgCount:0,hasUpdate:false,hasDot:true},{id:4,name:'商城',icon:'🛒',bgColor:'#059669',msgCount:0,hasUpdate:true,hasDot:false},{id:5,name:'日历',icon:'📅',bgColor:'#7C3AED',msgCount:12,hasUpdate:false,hasDot:false},{id:6,name:'设置',icon:'⚙️',bgColor:'#374151',msgCount:0,hasUpdate:true,hasDot:false},]@Statetabs:TabItem[]=[{label:'首页',icon:'🏠',badgeCount:0},{label:'消息',icon:'💬',badgeCount:5},{label:'发现',icon:'🔍',badgeCount:0},{label:'我的',icon:'👤',badgeCount:1},]@StateselectedTab:number=0@StatedotBadgeVisible:boolean=trueclearBadge(appId:number){this.apps=this.apps.map((app:AppInfo)=>{if(app.id===appId){return{id:app.id,name:app.name,icon:app.icon,bgColor:app.bgColor,msgCount:0,hasUpdate:false,hasDot:false}}returnapp})}build(){Column({space:0}){// 标题Text('Badge 徽标组件').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#111827').width('100%').padding({left:20,right:20,top:20,bottom:4})Text('数字徽标、圆点徽标、位置控制').fontSize(13).fontColor('#6B7280').width('100%').padding({left:20,right:20,bottom:16})// 一、应用图标徽标Column({space:12}){Text('一、应用图标徽标(点击清除)').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#374151').width('100%')Flex({wrap:FlexWrap.Wrap,justifyContent:FlexAlign.Start}){ForEach(this.apps,(app:AppInfo)=>{Column({space:6}){if(app.msgCount>0){Badge({count:app.msgCount,maxCount:99,position:BadgePosition.RightTop,style:{badgeSize:18,badgeColor:'#EF4444',fontSize:10}}){Text(app.icon).fontSize(28).width(56).height(56).textAlign(TextAlign.Center).backgroundColor(app.bgColor).borderRadius(14)}.onClick(()=>this.clearBadge(app.id))}elseif(app.hasDot){Badge({value:'',position:BadgePosition.RightTop,style:{badgeSize:10,badgeColor:'#EF4444'}}){Text(app.icon).fontSize(28).width(56).height(56).textAlign(TextAlign.Center).backgroundColor(app.bgColor).borderRadius(14)}.onClick(()=>this.clearBadge(app.id))}elseif(app.hasUpdate){Badge({value:'NEW',position:BadgePosition.RightTop,style:{badgeSize:16,badgeColor:'#F97316',fontSize:9,fontWeight:FontWeight.Bold}}){Text(app.icon).fontSize(28).width(56).height(56).textAlign(TextAlign.Center).backgroundColor(app.bgColor).borderRadius(14)}.onClick(()=>this.clearBadge(app.id))}else{Text(app.icon).fontSize(28).width(56).height(56).textAlign(TextAlign.Center).backgroundColor(app.bgColor).borderRadius(14).onClick(()=>this.clearBadge(app.id))}Text(app.name).fontSize(11).fontColor('#374151')}.alignItems(HorizontalAlign.Center).margin({right:16,bottom:8})},(app:AppInfo)=>app.id.toString())}.width('100%').padding({top:12,bottom:12,left:12,right:12}).backgroundColor('#F9FAFB').borderRadius(12)}.width('100%').padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:16})// 二、徽标位置对比Column({space:12}){Text('二、徽标位置对比').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#374151').width('100%')Row({space:20}){Column({space:8}){Badge({count:5,position:BadgePosition.RightTop,style:{badgeSize:18,badgeColor:'#EF4444'}}){Text('📦').fontSize(28).width(52).height(52).textAlign(TextAlign.Center).backgroundColor('#EFF6FF').borderRadius(12)}Text('右上角').fontSize(12).fontColor('#6B7280')}Column({space:8}){Badge({count:5,position:BadgePosition.Left,style:{badgeSize:18,badgeColor:'#059669'}}){Text('📦').fontSize(28).width(52).height(52).textAlign(TextAlign.Center).backgroundColor('#ECFDF5').borderRadius(12)}Text('左侧').fontSize(12).fontColor('#6B7280')}Column({space:8}){Badge({count:5,position:BadgePosition.Right,style:{badgeSize:18,badgeColor:'#7C3AED'}}){Text('📦').fontSize(28).width(52).height(52).textAlign(TextAlign.Center).backgroundColor('#F5F3FF').borderRadius(12)}Text('右侧').fontSize(12).fontColor('#6B7280')}}.width('100%').justifyContent(FlexAlign.SpaceAround).padding(12).backgroundColor('#F9FAFB').borderRadius(12)}.width('100%').padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:16})// 三、Tab 栏徽标Column({space:12}){Text('三、Tab 栏消息徽标').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#374151').width('100%')Row(){ForEach(this.tabs,(tab:TabItem,index:number)=>{Column({space:4}){if(tab.badgeCount>0){Badge({count:tab.badgeCount,position:BadgePosition.RightTop,style:{badgeSize:16,badgeColor:'#EF4444',fontSize:10}}){Text(tab.icon).fontSize(24).width(36).height(36).textAlign(TextAlign.Center)}}else{Text(tab.icon).fontSize(24).width(36).height(36).textAlign(TextAlign.Center)}Text(tab.label).fontSize(11).fontColor(this.selectedTab===index?'#2563EB':'#9CA3AF')}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick(()=>{this.selectedTab=index})},(tab:TabItem)=>tab.label)}.width('100%').padding({top:8,bottom:8}).backgroundColor(Color.White).borderRadius(12).shadow({radius:8,color:'rgba(0,0,0,0.08)',offsetY:-2})}.width('100%').padding({left:20,right:20}).alignItems(HorizontalAlign.Start)Blank()}.width('100%').height('100%').backgroundColor('#FFFFFF')}}写在最后
如果你正在整理 HarmonyOS7 组件案例,可以把这篇当成Badge的实践模板。先跑通页面,再逐步替换数据和样式,效率会高不少。
