前言
前面我们用layoutWeight让 HUD 三栏按 2:1:2 比例分宽——屏宽变化时各栏同比例缩放。但有个新问题:窄屏(折叠屏合屏、小尺寸手机)时,连击栏被挤成一条缝,文字溢出截断。我们想要的是:宽屏三栏全显示,窄屏自动隐藏低优先级的连击栏,只留得分和时间。
HarmonyOS 提供了displayPriority优先级布局——给每个子组件设一个优先级,容器空间不足时自动隐藏低优先级组件。本篇以「猫猫大作战」HUD 窄屏适配为锚点,把displayPriority的优先级机制、隐藏触发条件、与 layoutWeight 搭配三大要点讲透。
提示:本系列不讲 ArkTS 基础语法与环境搭建,假设你已跟完第 1–23 篇。本篇是布局进阶的第四篇。
一、场景拆解:HUD 窄屏适配
回顾「猫猫大作战」HUD(第 23 篇):
// 来源:entry/src/main/ets/pages/Index.ets GameHUD() 改造版 Row() { Column() { Text('得分'); Text(score) } .layoutWeight(2) Row() { Text(`🔥 x${combo}`) } .layoutWeight(1) Column() { Text('时间'); Text(time) } .layoutWeight(2) } .width('100%')痛点:
| 屏宽 | 得分栏 | 连击栏 | 时间栏 | 视觉 |
|---|---|---|---|---|
| 360vp(宽屏) | 144vp | 72vp | 144vp | ✅ 三栏舒服 |
| 280vp(窄屏) | 112vp | 56vp | 112vp | ❌ 连击栏 56vp 文字溢出 |
| 200vp(超窄) | 80vp | 40vp | 80vp | ❌ 连击栏 40vp 几乎看不见 |
我们想要:屏宽 < 300vp 时自动隐藏连击栏,得分和时间平分宽度。
displayPriority的解法:
Row() { Column() { /* 得分 */ }.layoutWeight(2).displayPriority(1) Row() { /* 连击 */ }.layoutWeight(1).displayPriority(2) // 优先级低,先被隐藏 Column() { /* 时间 */ }.layoutWeight(2).displayPriority(1) } .width('100%')关键经验:displayPriority值越大优先级越低,容器空间不足时先隐藏低优先级(高数值)组件。
二、displayPriority 优先级机制
2.1 优先级数值含义
.displayPriority(N) // N 是正整数| N 值 | 含义 | 隐藏顺序 |
|---|---|---|
| 1 | 最高优先级 | 最后被隐藏 |
| 2 | 次高优先级 | 第 2 个被隐藏 |
| 3 | 中等优先级 | 第 3 个被隐藏 |
| N | 优先级随 N 增大而降低 | — |
规则:容器空间不足时,按displayPriority从大到小依次隐藏;同优先级的组件一起隐藏或一起显示。
2.2 隐藏触发条件
displayPriority不是「屏宽小于某个值就隐藏」,而是更智能的自适应隐藏:
容器宽度 - 所有组件最小宽度之和 < 0 → 开始隐藏低优先级组件最小宽度来自组件的width、内容宽度、constraintSize.minWidth等。
举例(HUD 三栏):
| 组件 | 最小宽度 | displayPriority |
|---|---|---|
| 得分栏 | 100vp | 1 |
| 连击栏 | 60vp | 2 |
| 时间栏 | 100vp | 1 |
容器宽度变化时的行为:
| 容器宽度 | 最小宽度和 | 是否够 | 行为 |
|---|---|---|---|
| 360vp | 260vp | ✅ | 三栏全显示 |
| 280vp | 260vp | ✅ 勉强 | 三栏全显示(各栏接近最小宽) |
| 200vp | 260vp | ❌ 差 60vp | 隐藏连击栏(省 60vp),剩得分+时间 |
2.3 与 layoutWeight 的关系
Row() { Column() { /* 得分 */ }.layoutWeight(2).displayPriority(1) Row() { /* 连击 */ }.layoutWeight(1).displayPriority(2) Column() { /* 时间 */ }.layoutWeight(2).displayPriority(1) } .width('100%')执行顺序:
- 先评估
displayPriority——决定哪些组件显示、哪些隐藏。 - 再算
layoutWeight——对显示的组件按权重分宽。
关键经验:displayPriority决定「显示与否」,layoutWeight决定「显示后分多少宽」——两者协同工作。
三、用 displayPriority 改造 HUD
3.1 改造为窄屏自适应隐藏
@Builder GameHUD() { Row() { // 左:得分(最高优先级,永远显示) Column() { Text('得分') .fontSize(11) .fontColor('#95A5A6') Text(this.score.toString()) .fontSize(22) .fontWeight(FontWeight.Bold) .fontColor('#2C3E50') } .layoutWeight(2) .displayPriority(1) // 优先级 1:最后被隐藏 .alignItems(HorizontalAlign.Start) // 中:连击(低优先级,窄屏先隐藏) if (this.combo.count > 1) { Row() { Text(`🔥 x${this.combo.multiplier}`) .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor('#E74C3C') } .layoutWeight(1) .displayPriority(2) // 优先级 2:窄屏先隐藏 .justifyContent(FlexAlign.Center) .padding({ left: 8, right: 8, top: 4, bottom: 4 }) .backgroundColor('rgba(231, 76, 60, 0.1)') .borderRadius(16) } // 右:时间(最高优先级,永远显示) Column() { Text('时间') .fontSize(11) .fontColor('#95A5A6') Text(this.formatTime(this.gameTime)) .fontSize(18) .fontWeight(FontWeight.Medium) .fontColor('#2C3E50') } .layoutWeight(2) .displayPriority(1) // 优先级 1:最后被隐藏 .alignItems(HorizontalAlign.End) } .width('100%') .padding({ left: 20, right: 20, top: 12, bottom: 8 }) }改造效果:
| 屏宽 | 得分 | 连击 | 时间 | 行为 |
|---|---|---|---|---|
| ≥ 300vp | 2/5 | 1/5 | 2/5 | ✅ 三栏全显示 |
| < 300vp | 1/2 | ❌隐藏 | 1/2 | ✅ 连击隐藏,得分时间平分 |
3.2 关键:连击栏 displayPriority(2)
.displayPriority(2) // 数值大 = 优先级低 = 先被隐藏逻辑:连击是「锦上添花」信息(连击数 + 倍率),得分和时间是「必须可见」的核心信息。窄屏时优先隐藏连击。
3.3 隐藏后的宽度重分配
连击栏隐藏后,Row 重新计算layoutWeight:
// 隐藏前:2 + 1 + 2 = 5,得分 2/5、连击 1/5、时间 2/5 // 隐藏后:2 + 2 = 4,得分 2/4=1/2、时间 2/4=1/2得分和时间各占 50% 宽度,布局自动重平衡。
四、displayPriority 多组件场景
4.1 场景:游戏结束弹窗 4 个统计项
// 来源:entry/src/main/ets/pages/Index.ets GameOverOverlay() Row() { this.StatItem('时间', this.formatTime(this.gameTime)) this.StatItem('合并', `${this.mergeCount}次`) this.StatItem('最高连击', `x${this.maxCombo}`) } .width('100%') .justifyContent(FlexAlign.SpaceEvenly)3 个统计项平铺,窄屏时文字会重叠。用displayPriority让低优先级的「最高连击」先隐藏:
Row() { this.StatItem('时间', this.formatTime(this.gameTime)) .displayPriority(1) // 必须:时间永远显示 this.StatItem('合并', `${this.mergeCount}次`) .displayPriority(2) // 次要:窄屏第 2 个隐藏 this.StatItem('最高连击', `x${this.maxCombo}`) .displayPriority(3) // 最次要:窄屏第 1 个隐藏 } .width('100%') .justifyContent(FlexAlign.SpaceEvenly)窄屏隐藏顺序:
| 屏宽 | 时间 | 合并 | 最高连击 | 行为 |
|---|---|---|---|---|
| 宽屏 | ✅ | ✅ | ✅ | 全显示 |
| 中屏 | ✅ | ✅ | ❌ | 隐藏最高连击 |
| 窄屏 | ✅ | ❌ | ❌ | 隐藏合并 + 最高连击 |
4.2 同优先级组件
Row() { Text('A').displayPriority(1) Text('B').displayPriority(1) // 与 A 同优先级 Text('C').displayPriority(2) // 低优先级 } // 窄屏时 C 先隐藏;再窄时 A、B 一起隐藏关键经验:同优先级组件「同生共死」——要么一起显示,要么一起隐藏。
五、displayPriority vs visibility vs if/else
ArkUI 有三种「隐藏组件」的方式,容易混淆:
5.1 三者对比
| 方式 | 触发 | 占布局空间 | 适合 |
|---|---|---|---|
displayPriority | 容器空间不足自动触发 | 隐藏后不占,其他组件重分配 | 自适应窄屏 |
visibility(Visibility.Hidden) | 开发者手动设 | 隐藏后仍占空间 | 条件占位 |
if/else | 条件渲染 | 隐藏后不占,但需重新渲染 | 状态切换 |
5.2 场景对照
// 场景 1:窄屏自动隐藏连击 → displayPriority Row() { Column().displayPriority(1) Row().displayPriority(2) // 窄屏自动隐藏 Column().displayPriority(1) } // 场景 2:加载时占位但不显示 → visibility Text('加载中...').visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden) // 场景 3:登录态切换不同界面 → if/else if (this.isLoggedIn) { MainPage() } else { LoginPage() }关键经验:displayPriority是唯一的「自适应」隐藏——其他两种都是开发者显式控制。
5.3 displayPriority 的局限
displayPriority只能「隐藏」,不能「折叠样式」——比如窄屏时想把三栏改成纵向排列,displayPriority做不到,得用响应式布局(GridRow/GridCol,本系列第 88 篇会讲)。
六、完整代码:displayPriority 版 HUD
// 改造版:用 displayPriority 让窄屏自动隐藏连击栏 @Builder GameHUD() { Row() { // 左:得分(最高优先级,永远显示) Column() { Text('得分') .fontSize(11) .fontColor('#95A5A6') Text(this.score.toString()) .fontSize(22) .fontWeight(FontWeight.Bold) .fontColor('#2C3E50') } .layoutWeight(2) .displayPriority(1) .alignItems(HorizontalAlign.Start) // 中:连击(低优先级,窄屏先隐藏) if (this.combo.count > 1) { Row() { Text(`🔥 x${this.combo.multiplier}`) .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor('#E74C3C') } .layoutWeight(1) .displayPriority(2) .justifyContent(FlexAlign.Center) .padding({ left: 8, right: 8, top: 4, bottom: 4 }) .backgroundColor('rgba(231, 76, 60, 0.1)') .borderRadius(16) } // 右:时间(最高优先级,永远显示) Column() { Text('时间') .fontSize(11) .fontColor('#95A5A6') Text(this.formatTime(this.gameTime)) .fontSize(18) .fontWeight(FontWeight.Medium) .fontColor('#2C3E50') } .layoutWeight(2) .displayPriority(1) .alignItems(HorizontalAlign.End) } .width('100%') .padding({ left: 20, right: 20, top: 12, bottom: 8 }) }七、踩坑提示
7.1 displayPriority 与 width 冲突
// ❌ 错误:固定 width + displayPriority,width 优先,displayPriority 不触发 Text('A').width(80).displayPriority(2) // ✅ 正确:displayPriority 配合 layoutWeight 或不设 width Text('A').layoutWeight(1).displayPriority(2)7.2 所有组件同 displayPriority
// ⚠️ 全部 displayPriority(1):同优先级,窄屏一起隐藏 Row() { Text('A').displayPriority(1) Text('B').displayPriority(1) Text('C').displayPriority(1) } // 窄屏时 A、B、C 一起消失,HUD 变空白解法:核心信息设displayPriority(1),次要信息设更高数值。
7.3 displayPriority 不触发
// 现象:屏宽已经很窄,但连击栏还在显示 // 排查:连击栏最小宽度可能很小(文字短),容器空间够,不触发隐藏 // 解法:用 constraintSize 设最小宽度,强制触发 Row().constraintSize({ minWidth: 80 }).displayPriority(2)八、调试技巧
- DevEco 多设备预览:同时看手机/折叠屏/平板预览,验证不同屏宽下的隐藏行为。
- 临时加背景色:给每个 displayPriority 组件加不同背景色,看清隐藏顺序。
console.info监屏宽:window.on('windowSizeChange')打 log,追屏宽变化与隐藏触发点。
九、性能与最佳实践
- 窄屏自适应用
displayPriority——比手动 if/else 响应屏宽更优雅。 - 核心信息 displayPriority(1),次要信息递增——保证核心永远可见。
- displayPriority 配 layoutWeight——隐藏后剩余组件按权重重新分配。
- 同优先级组件同生共死——要么一起显示,要么一起隐藏。
- 需要重新排列(横→竖)用 GridRow——displayPriority 只能隐藏不能重排。
总结
本篇我们从 displayPriority 切入,掌握了优先级数值含义(大值低优先级先隐藏)、隐藏触发条件(容器空间不足时)、与 layoutWeight/visibility/if-else 的取舍三大要点,并给出了 displayPriority 版 HUD 完整代码。核心要点:displayPriority 自适应窄屏隐藏;核心信息优先级 1;隐藏后 layoutWeight 重分配。
下一篇我们将拆解 Scroller——长内容滚动容器。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- 「猫猫大作战」项目源码:本仓库
entry/src/main/ets/pages/Index.ets - displayPriority 优先级布局官方指南
- visibility 显隐控制官方指南
- 多端布局自适应最佳实践
- 开源鸿蒙跨平台社区
- HarmonyOS 开发者官方文档首页
- 系列索引:本仓库
articles/INDEX.md