文章目录前言一、Stack 基础概念1.1 Stack 的工作原理1.2 alignContent 对齐方式二、本项目中的 Stack地图浮层2.1 源码分析2.2 titleBuilder 中的绝对定位三、实战带角标的图标组件3.1 消息角标四、实战悬浮操作按钮FAB4.1 固定位置的悬浮按钮五、zIndex精细控制层级5.1 用 zIndex 改变默认层叠顺序六、position 绝对定位详解6.1 position vs offset 的区别总结前言在 HarmonyOS 应用中你经常会看到这样的效果地图上漂浮着一个定位按钮、图片右上角有个红点角标、页面底部有个悬浮的返回顶部按钮……这些叠加在其他内容上方的 UI都离不开Stack 层叠布局。本篇通过实战案例带你掌握 Stack 的核心用法并深入讲解本项目地图浮层的实现原理。一、Stack 基础概念1.1 Stack 的工作原理Stack 是一个叠罗汉容器所有子组件都叠放在同一位置后定义的组件显示在前面层级更高。EntryComponentstruct BasicStackDemo{build(){Stack(){// 最先定义 → 在最底层Text(底层蓝色背景).width(200).height(200).backgroundColor(#1A6FF5).fontSize(14).fontColor(#FFFFFF)// 中间定义 → 在中间层Text(中层黄色小一些).width(150).height(150).backgroundColor(#FA8C16).fontSize(13).fontColor(#FFFFFF)// 最后定义 → 在最顶层Text(顶层绿色最小).width(100).height(100).backgroundColor(#52C41A).fontSize(12).fontColor(#FFFFFF)}.width(100%).height(300).alignContent(Alignment.Center)// 所有子组件居中对齐}}1.2 alignContent 对齐方式值说明Alignment.TopStart左上角Alignment.Top顶部居中Alignment.TopEnd右上角Alignment.Start左侧居中Alignment.Center正中心默认Alignment.End右侧居中Alignment.BottomStart左下角Alignment.Bottom底部居中Alignment.BottomEnd右下角二、本项目中的 Stack地图浮层2.1 源码分析GasStationPage.ets的build()方法build(){NavDestination(){Stack(){// 层1底层地图组件MapComponent({mapOptions:this.mapOptions,mapCallback:this.callback,});// 层2顶层自定义标题栏this.titleBuilder();}.width(Constants.FULL_PERCENT).height(Constants.FULL_PERCENT).bindSheet($$this.isShow,this.bindBuilder(),{/* ... */})}// ...}Stack 的两个子组件MapComponent地图底层铺满整个 Stackthis.titleBuilder()返回按钮和标题顶层覆盖在地图上方这就是为什么地图上能显示返回按钮——它通过 Stack 层叠在地图之上。2.2 titleBuilder 中的绝对定位BuildertitleBuilder(){Row({space:Constants.SPACE_8}){Image($r(app.media.back)).width(Constants.IMAGE_BACK_WIDTH).height(Constants.IMAGE_BACK_HEIGHT).onClick((){this.pageInfos.pop();});Text($r(app.string.car_life)).fontWeight(Constants.FONT_WEIGHT_700).fontSize(Constants.FONT_SIZE_20)// ...}.width(Constants.FULL_PERCENT).padding({left:Constants.PADDING_RIGHT_16}).position({top:Constants.POSITION_TOP// 绝对定位到顶部});}position({ top: xx })是绝对定位让 titleBuilder 固定在 Stack 顶部而不是随内容流动。三、实战带角标的图标组件3.1 消息角标Componentstruct BadgeIcon{PropiconRes:Resource$r(app.media.startIcon);PropbadgeCount:number0;PropbadgeVisible:booleantrue;build(){Stack({alignContent:Alignment.TopEnd}){// 底层图标Image(this.iconRes).width(36).height(36)// 顶层角标右上角if(this.badgeVisiblethis.badgeCount0){Text(this.badgeCount99?99:${this.badgeCount}).fontSize(10).fontColor(#FFFFFF).backgroundColor(#FF4D4F).borderRadius(10).padding({left:4,right:4,top:1,bottom:1}).height(18).textAlign(TextAlign.Center).offset({x:6,y:-6})// 微调位置让角标稍微超出图标边界}}.width(40).height(40)}}EntryComponentstruct BadgeDemoPage{StatemsgCount:number5;build(){Column({space:32}){Text(角标示例).fontSize(18).fontWeight(FontWeight.Bold)Row({space:32}){BadgeIcon({iconRes:$r(app.media.startIcon),badgeCount:this.msgCount,badgeVisible:true})BadgeIcon({iconRes:$r(app.media.startIcon),badgeCount:128,badgeVisible:true})BadgeIcon({iconRes:$r(app.media.startIcon),badgeCount:0,badgeVisible:false})}// 控制角标数量Row({space:12}){Button(-1).onClick((){if(this.msgCount0)this.msgCount--}).width(60).height(36).backgroundColor(#FF4D4F).fontColor(#FFF)Text(消息${this.msgCount}).fontSize(16)Button(1).onClick((){this.msgCount}).width(60).height(36).backgroundColor(#52C41A).fontColor(#FFF)}}.width(100%).height(100%).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}}四、实战悬浮操作按钮FAB4.1 固定位置的悬浮按钮EntryComponentstruct FABDemo{Stateitems:string[][加油站1,加油站2,加油站3,加油站4,加油站5,加油站6,加油站7,加油站8,加油站9,加油站10,加油站11,加油站12,加油站13,加油站14,加油站15,加油站16,加油站17,加油站18,加油站19,加油站20]StateshowFAB:booleantrue;privatescroller:ScrollernewScroller();build(){// 必须用 Stack 才能让 FAB 浮在列表上方Stack({alignContent:Alignment.BottomEnd}){// 底层列表内容List(){ForEach(this.items,(item:string,index:number){ListItem(){Row({space:12}){Text(⛽).fontSize(20)Column({space:4}){Text(item).fontSize(16).fontWeight(FontWeight.Medium)Text(点击查看详情).fontSize(12).fontColor(#999999)}.alignItems(HorizontalAlign.Start).layoutWeight(1)Text(${(index1)*0.30.5}km).fontSize(13).fontColor(#1A6FF5)}.padding(16).width(100%).backgroundColor(#FFFFFF).margin({bottom:8}).borderRadius(12)}})}.padding({left:16,right:16,top:16,bottom:80})// 底部留出 FAB 空间.width(100%).height(100%)// 顶层悬浮按钮if(this.showFAB){Column({space:8}){// 定位按钮Button(){Text().fontSize(20)}.width(52).height(52).borderRadius(26).backgroundColor(#FFFFFF).shadow({radius:8,color:#30000000,offsetX:0,offsetY:4})// 添加按钮Button(){Text().fontSize(28).fontColor(#FFFFFF)}.width(56).height(56).borderRadius(28).backgroundColor(#1A6FF5).shadow({radius:8,color:#401A6FF5,offsetX:0,offsetY:4})}.position({right:16,bottom:24})// 固定在右下角}}.width(100%).height(100%).backgroundColor(#F5F7FA)}}五、zIndex精细控制层级5.1 用 zIndex 改变默认层叠顺序EntryComponentstruct ZIndexDemo{build(){Stack(){// 定义顺序A 先B 后B 应该在 A 上面// 但用 zIndex 让 A 显示在 B 上面Text(A先定义zIndex10).width(180).height(180).backgroundColor(#FF4D4F).fontColor(#FFFFFF).fontSize(14).textAlign(TextAlign.Center).zIndex(10)// 手动提高层级Text(B后定义zIndex0).width(180).height(180).backgroundColor(#1A6FF5).fontColor(#FFFFFF).fontSize(14).textAlign(TextAlign.Center).offset({x:40,y:40}).zIndex(0)}.width(100%).height(300).backgroundColor(#F5F7FA)}}提示zIndex越大显示层级越高。默认值为 0自然层叠顺序是后定义的组件在上。六、position 绝对定位详解6.1 position vs offset 的区别// position相对于父容器的左上角定位绝对定位.position({x:16,y:100})// offset相对于组件自身正常位置的偏移相对定位.offset({x:10,y:-5})EntryComponentstruct PositionDemo{build(){Stack(){// 铺满底层背景Column().width(100%).height(100%).backgroundColor(#F0F4FF)// 绝对定位固定在左上角Text(左上角).padding(8).backgroundColor(#FF4D4F).fontColor(#FFF).borderRadius(8).position({x:16,y:16})// 绝对定位固定在右下角Text(右下角).padding(8).backgroundColor(#52C41A).fontColor(#FFF).borderRadius(8).position({right:16,bottom:16})// 绝对定位固定在正中心偏下Text(中心偏下).padding(8).backgroundColor(#1A6FF5).fontColor(#FFF).borderRadius(8).position({x:40%,y:60%})}.width(100%).height(400)}}总结Stack 是 ArkUI 实现层叠效果的核心容器地图浮层、角标徽标、悬浮按钮……都是 Stack 的典型应用场景。结合position绝对定位和zIndex层级控制你可以构建任意复杂的叠加 UI 结构。本项目中MapComponenttitleBuilder的叠加方案是一个教科书式的 Stack 实战案例值得反复学习。