【共创季稿事节】鸿蒙原生 ArkTS 布局深度解析:Stack 实现圆角头像与角标

📅 2026/6/22 9:00:54
【共创季稿事节】鸿蒙原生 ArkTS 布局深度解析:Stack 实现圆角头像与角标
鸿蒙原生 ArkTS 布局深度解析Stack 实现圆角头像与角标一、前言在移动应用开发中圆形头像 在线状态角标是社交类、即时通讯类 App 中最常见的 UI 模式之一。微信、Telegram、Discord 等主流应用无一例外地采用了这一视觉范式。对于鸿蒙原生开发者而言如何在 HarmonyOS NEXT 上使用 ArkTS 高效、优雅地实现这一布局是掌握鸿蒙 UI 开发的第一道关卡。本文将从一个完整的可运行示例出发深入剖析Stack布局容器在圆角头像与角标场景下的三种实现方案并详细讲解.borderRadius切圆、Badge组件内置角标、Circle手绘状态指示器、Component子组件封装等核心技术。全文配以完整源码和中文注释力求让读者「看得懂、学得会、用得顺」。二、环境与工程配置2.1 开发环境要求项目要求操作系统Windows 10 / macOS 13IDEDevEco Studio 5.1SDKHarmonyOS NEXT API 24HarmonyOS 6.2构建工具hvigor 内置在 DevEco Studio 中目标设备手机 / 平板 / 模拟器2.2 工程结构app6211/ ├── AppScope/ # 应用级配置 ├── entry/ │ └── src/main/ets/ │ ├── entryability/ # Ability 生命周期 │ └── pages/ │ └── Index.ets # ★ 核心页面本文分析对象 ├── build-profile.json5 # 应用级构建配置 └── hvigor/ # 构建系统在build-profile.json5中我们配置了 SDK 版本为 API 24{ app: { products: [ { targetSdkVersion: 6.2.0(24), compatibleSdkVersion: 6.2.0(24), runtimeOS: HarmonyOS } ] } }三、核心布局组件概览3.1 Stack — 层叠布局容器Stack是 ArkUI 提供的层叠布局容器其核心特点是子组件按照在代码中的书写顺序从底向上依次堆叠后声明的组件在 Z 轴上覆盖先声明的组件。Z 轴方向从上往下看 ┌─────────────────────┐ │ 顶层子组件最后声明 │ ← Z-index 最高 ├─────────────────────┤ │ 中间层子组件 │ ├─────────────────────┤ │ 底层子组件最先声明 │ ← Z-index 最低 └─────────────────────┘Stack不限制子组件的数量每个子组件可以通过以下方式定位.position({ x, y })相对于 Stack 左上角的绝对偏移.align(Alignment.xxx)相对于 Stack 容器的对齐方式默认居中如果子组件未指定任何位置属性默认在 Stack 中心3.2 Badge — 系统内置角标组件Badge是 HarmonyOS NEXT 内置的角标容器专门用于在子元素右上角或其他位置显示小圆点或数字标记。它的构造参数如下参数类型说明valuestring | number角标内容。空字符串表示纯色圆点positionBadgePosition角标位置RightTop,Right,RightBottom等style.badgeSizenumber角标圆点直径单位 vpstyle.badgeColorstring角标背景色style.fontSizenumber可选数字角标的字体大小style.fontWeightnumber | FontWeight可选数字角标的字重3.3 Circle — 形状绘制组件Circle()是 ArkUI 提供的原生形状组件用于绘制圆形。与Image不同它不依赖图片资源完全由属性驱动渲染Circle().width(24)// 圆直径.height(24)// 圆直径.fill(#4CAF50)// 填充色.stroke(#FFFFFF)// 描边色.strokeWidth(3)// 描边宽度这一特性使得Circle天然适合作为状态指示器 —— 轻量、高效、无网络请求。四、三种实现方案详解我们的示例代码Index.ets演示了三种递进式实现方案覆盖从「开箱即用」到「完全自定义」的全场景需求。方案一Badge 组件内置角标推荐适用场景标准圆形头像 简单状态指示追求开发效率。Badge({value:,// 空串 → 纯色圆点position:BadgePosition.RightTop,// 右上角style:{badgeSize:18,// 角标直径 18vpbadgeColor:#4CAF50// 在线绿色}}){Image($r(app.media.startIcon)).width(80).height(80).borderRadius(40)// 切圆宽高的一半.objectFit(ImageFit.Cover).border({width:2,color:#FFFFFF})}关键要点Badge作为容器包裹头像自动在右上角叠加角标.borderRadius(40)将 80×80 的正方形图片裁切成正圆 ——半径 宽/2.objectFit(ImageFit.Cover)确保图片内容按比例填充不留黑边白色描边.border({ width: 2, color: #FFFFFF })增加视觉层次感扩展用法 —— 数字角标当value传入非空字符串如3时Badge 自动切换为数字角标样式适合展示未读消息数Badge({value:3,// 显示数字position:BadgePosition.RightTop,style:{badgeSize:20,badgeColor:#FF5722,// 红色 提醒fontSize:11,fontWeight:FontWeight.Bold}}){Image($r(app.media.startIcon)).width(64).height(64).borderRadius(32)}方案二纯 Stack 手动叠加角标灵活控制适用场景需要将角标放在任意位置左上、左下、右下或状态圆点有特殊样式需求。Stack(){// 第1层底层圆形头像Image($r(app.media.startIcon)).width(96).height(96).borderRadius(48).objectFit(ImageFit.Cover).border({width:2,color:#FFFFFF})// 第2层顶层右下角在线状态圆点Circle().width(24).height(24).fill(#4CAF50).stroke(#FFFFFF).strokeWidth(3).position({x:72,y:72})// 96-2472右下角对齐}.width(96).height(96)定位计算口诀position.x 头像宽 - 圆点宽 position.y 头像高 - 圆点高将此公式推广到四个角落角标位置posXposY左上角00右上角头像宽 - 圆点宽0左下角0头像高 - 圆点高右下角头像宽 - 圆点宽头像高 - 圆点高多状态演示在示例代码中我们通过封装StatusAvatar子组件一行代码即可展示不同状态Row({space:24}){StatusAvatar({avatarSize:56,statusColor:#FFC107,dotSize:14,posX:0,posY:0})// 离开-黄StatusAvatar({avatarSize:56,statusColor:#4CAF50,dotSize:14,posX:42,posY:0})// 在线-绿StatusAvatar({avatarSize:56,statusColor:#9E9E9E,dotSize:14,posX:42,posY:42})// 离线-灰StatusAvatar({avatarSize:56,statusColor:#F44336,dotSize:14,posX:0,posY:42})// 忙碌-红}这种「颜色语义化」的设计模式让代码意图一目了然也便于后期维护和扩展。方案三大尺寸用户卡片综合应用适用场景个人主页、用户详情页需要展示大尺寸头像 状态 姓名标签的综合信息卡片。Stack(){// 底层圆形头像 120×120Image($r(app.media.startIcon)).width(120).height(120).borderRadius(60).border({width:3,color:#FFFFFF})// 顶层右下角状态圆点Circle().width(30).height(30).fill(#4CAF50).stroke(#FFFFFF).strokeWidth(4).position({x:90,y:90})// 顶层底部居中半透明姓名标签Row(){Text(张三).fontSize(14).fontColor(#FFFFFF).fontWeight(FontWeight.Bold)}.width(100%).height(32).backgroundColor(#66000000)// 半透明黑底.justifyContent(FlexAlign.Center).align(Alignment.Bottom)// 贴在 Stack 底部}.width(120).height(120).borderRadius(12)方案三的亮点三层叠加头像 → 状态圆点 → 姓名标签全部在一个 Stack 内完成混合定位状态圆点用.position()绝对定位姓名标签用.align(Alignment.Bottom)相对定位半透明遮罩#66000000是 40% 透明度的黑色文字清晰可读五、子组件封装与复用为了提升代码的可维护性示例将三个可复用 UI 单元抽取为独立的Component5.1 TitleBar — 顶部标题栏Componentstruct TitleBar{build(){Row(){Text( 圆角头像 角标).fontSize(20).fontWeight(FontWeight.Bold).fontColor(#FFFFFF)}.width(100%).height(56).backgroundColor(#3F51B5).padding({left:20})}}5.2 SectionTitle — 区块标题Componentstruct SectionTitle{Proptitle:string;Propdesc:string;build(){Column({space:4}){Text(this.title).fontSize(15).fontWeight(FontWeight.Bold).fontColor(#333)Text(this.desc).fontSize(12).fontColor(#999)}.alignItems(HorizontalAlign.Start).width(100%).margin({top:8})}}5.3 StatusAvatar — 可复用头像角标核心组件Componentstruct StatusAvatar{PropavatarSize:number56;PropstatusColor:string#4CAF50;PropdotSize:number14;PropposX:number42;PropposY:number42;build(){Stack(){Image($r(app.media.startIcon)).width(this.avatarSize).height(this.avatarSize).borderRadius(this.avatarSize/2)// 动态切圆.objectFit(ImageFit.Cover).border({width:2,color:#FFFFFF})Circle().width(this.dotSize).height(this.dotSize).fill(this.statusColor).stroke(#FFFFFF).strokeWidth(2).position({x:this.posX,y:this.posY})}.width(this.avatarSize).height(this.avatarSize)}}封装价值一次定义多处复用 —— 仅需一行StatusAvatar {...} /即可创建完整的头像组件参数化驱动 ——avatarSize、statusColor、dotSize、posX、posY五个参数覆盖所有常见变体零外部依赖 —— 不依赖任何图片资源以外的外部模块可直接放入任何项目使用六、布局要点总结回顾整个实现过程以下是经过实战检验的五点核心经验6.1 Stack 是层叠容器子组件从底向上堆叠Stack的行为可以理解为「后入者在 Z 轴上越靠前」。这一特性让它在实现「叠加效果」时无需任何 z-index 或 elevation 设置天然适合头像角标、卡片遮罩、图片文字标签等场景。6.2 .borderRadius 切圆的数学公式将正方形图片切成正圆半径公式非常简单.borderRadius(图片宽度 / 2)对于 80×80 的图片 →.borderRadius(40)对于 96×96 的图片 →.borderRadius(48)对于 120×120 的图片 →.borderRadius(60)6.3 Badge 组件适合标准场景如果角标只需要在右上角展示纯色圆点或数字优先使用Badge组件。它是系统级实现经过充分优化代码量最少无需手动计算偏移。6.4 Stack .position 手动定位适合复杂场景当角标需要在任意位置出现左上、左下、右下、甚至中心或状态圆点有自定义动画、渐变等特殊需求时使用Stack Circle .position方案更灵活。6.5 Circle 是轻量级状态指示器Circle()不涉及图片加载、解码、缓存性能远优于同等尺寸的Image。配合.fill和.stroke可以绘制出任意颜色、任意描边效果的圆点非常适合作为在线/离开/离线/忙碌等状态指示器。七、性能优化建议7.1 图片资源管理头像图片建议使用WebP格式体积比 PNG 小 25%~35%在Image上设置.objectFit(ImageFit.Cover)可以避免图片变形对于列表中的大量头像考虑使用LazyForEach 内存缓存7.2 避免不必要的重绘角标颜色、位置等属性应使用Prop而非State传入子组件减少状态管理开销如果角标状态频繁变化如在线/离线切换使用Watch监听状态变化而非每秒重建 UI7.3 Stack 的嵌套深度避免 Stack 多层嵌套超过 5 层过深的嵌套会影响布局计算性能本示例中每个头像的 Stack 深度仅为 2 层外层 Stack 头像/圆点这是最优深度八、适用场景扩展本文所介绍的技术不局限于头像角标以下场景同样适用场景实现思路电商商品角标Stack Badge(value: ‘新品’) 叠加在商品图上视频播放器控件Stack 叠加播放/暂停按钮在视频上方图片编辑标注Stack Circle .position 标注人脸或物体位置地图标记点Stack 叠加自定义标记在 Map 组件上用户卡片堆叠多层 Stack 模拟卡片堆叠如探探式卡片九、完整源码完整的Index.ets源码已上传至项目entry/src/main/ets/pages/目录。核心代码约 273 行包含1 个主页面Component3 个可复用子组件3 套布局方案详尽的中文注释使用 DevEco Studio 打开项目后直接运行即可看到效果。页面支持滚动浏览全部三种方案及布局要点总结。十、结语通过本文的深入剖析我们从「Stack 层叠布局」出发逐步深入到 Badge 组件、Circle 形状绘制、Component 组件化封装等 HarmonyOS NEXT 原生 ArkTS 核心技术。这不仅仅是关于「如何画一个圆角头像」的教程更是一次关于鸿蒙原生声明式 UI 设计哲学的实践。在鸿蒙生态快速发展的今天掌握这些基础但强大的布局能力是每一位鸿蒙开发者构建高品质用户体验的基石。希望本文能为你的鸿蒙开发之旅提供实实在在的帮助。