查看: 102|回复: 1

HarmonyOS 6.1 List/Grid空分支占位特性深度解析与实践

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
在现代移动应用开发中,长列表的渲染性能直接决定了应用的流畅度。HarmonyOS的ArkUI框架通过LazyForEach和Repeat组件实现了高效的虚拟滚动,但在过去,当列表项的条件分支返回空时(例如广告位隐藏),开发者不得不使用高度为0的ListItem或visibility(None)等“脏方案”来避免渲染引擎报错,这不仅增加了无效节点开销,还污染了UI结构。

HarmonyOS NEXT 6.1 (API 23) 从框架层面解决了这一痛点:为List和Grid组件新增了supportEmptyBranchInLazyLoading特性。该特性允许在惰性加载中合法返回空分支,底层自动注入一个零尺寸虚拟代理对象(高度/宽度恒为0vp),并正确参与列表的排布计算,从而保证后续节点的坐标准确无误,滑动依然丝滑。

API使用方面,该接口挂载在ListAttribute和GridAttribute上:
  1. supportEmptyBranchInLazyLoading(supported: boolean | undefined)
复制代码
参数为true时启用空分支支持,false或undefined则回退旧版行为。需注意:这是一个“初次赋值不可变属性”,必须在第一帧写入后固定,后续无法通过状态变量动态切换。若需修改,只能销毁并重建组件树。

对于使用Native C-API的团队,ArkUI提供了对应的枚举值:
- List: NODE_LIST_SUPPORT_EMPTY_BRANCH_IN_LAZY_LOADING
- Grid: NODE_GRID_SUPPORT_EMPTY_BRANCH_IN_LAZY_LOADING

此外,该特性同样适用于开启了virtualScroll: true的Repeat组件,因为Repeat底层与LazyForEach共用一套虚拟滚动逻辑。若Repeat未开启虚拟滚动,则行为降级为普通ForEach,该配置不生效。

实战中,我们构建了一个动态广告位智能收缩面板Demo:一个包含100条数据的信息流,每逢5或7的倍数项模拟为过期广告位(需要隐藏)。在API 23下,我们只需在if条件中直接留空else分支,无需额外占位组件。代码如下:
  1. import { router } from '@kit.ArkUI';
  2. class SimpleDataSource implements IDataSource {
  3.   private list: number[] = [];
  4.   private listeners: DataChangeListener[] = [];
  5.   constructor(count: number) {
  6.     for (let i = 1; i <= count; i++) {
  7.       this.list.push(i);
  8.     }
  9.   }
  10.   totalCount(): number {
  11.     return this.list.length;
  12.   }
  13.   getData(index: number): number {
  14.     return this.list[index];
  15.   }
  16.   registerDataChangeListener(listener: DataChangeListener): void {
  17.     if (this.listeners.indexOf(listener) < 0) {
  18.       this.listeners.push(listener);
  19.     }
  20.   }
  21.   unregisterDataChangeListener(listener: DataChangeListener): void {
  22.     const pos = this.listeners.indexOf(listener);
  23.     if (pos >= 0) {
  24.       this.listeners.splice(pos, 1);
  25.     }
  26.   }
  27. }
  28. @Entry
  29. @Component
  30. struct LazyEmptyBranchDetail {
  31.   private dataSource: SimpleDataSource = new SimpleDataSource(100);
  32.   @State isEnableEmptyBranch: boolean = true;
  33.   @State currentLog: string = '核心管线就绪,等待观察0尺寸排版表现。';
  34.   isAdSlotNeedHide(val: number): boolean {
  35.     // 每5或7的倍数项隐藏
  36.     return val % 5 === 0 || val % 7 === 0;
  37.   }
  38.   build() {
  39.     Column() {
  40.       // 顶栏略...
  41.       List({ space: 12 }) {
  42.         LazyForEach(this.dataSource, (item: number) => {
  43.           if (!this.isAdSlotNeedHide(item)) {
  44.             ListItem() {
  45.               // 正常卡片内容
  46.               Row() {
  47.                 Text(`流水项 ID: #${item}`).fontColor('#FFFFFF').fontSize(16);
  48.                 Blank();
  49.                 Text('ACTIVE').fontColor('#4CAF50').fontSize(12);
  50.               }
  51.             }
  52.           } else {
  53.             if (this.isEnableEmptyBranch) {
  54.               // 空分支:什么都不渲染,底层自动处理为0尺寸节点
  55.             } else {
  56.               // 兼容模式:需要占位节点
  57.               ListItem() {
  58.                 Text(`[兼容层占位] ID #${item}`).fontColor('#666').fontSize(11);
  59.               }
  60.             }
  61.           }
  62.         }, (item: number) => item.toString())
  63.       }
  64.       .width('100%')
  65.       .layoutWeight(1)
  66.       // 正式启用时,取消注释以下属性
  67.       // .supportEmptyBranchInLazyLoading(true)
  68.       .edgeEffect(EdgeEffect.Spring)
  69.       .scrollBar(BarState.On)
  70.     }
  71.     .height('100%')
  72.     .width('100%')
  73.     .backgroundColor('#000000')
  74.   }
  75. }
复制代码
运行效果:开启空分支特性后,隐藏项的位置自动压缩为0高度,相邻卡片紧密衔接,滚动流畅;关闭后则出现多余的占位节点,破坏视觉一致性。

避坑指南:
1. 全局space属性可能导致连续空分支之间产生多余间距。例如连续3个空项且space=12,会形成36vp的空白区域。解法是将间距从父容器space改为每个ListItem的margin。
2. 初次赋值铁律:supportEmptyBranchInLazyLoading不能在运行时动态切换。若需从支持切换到不支持,必须销毁并重建List/Grid组件。建议在全局配置中写死为true。

总结:API 23的空分支支持从框架层面消除了条件渲染的脏逻辑,让长列表开发更符合直觉,显著提升了电商瀑布流、动态feed等场景的开发体验。鸿蒙引擎正在朝着精细化的排版哲学迈进。
回复

使用道具 举报

发表于 3 小时前 | 显示全部楼层

Re: HarmonyOS 6.1 List/Grid空分支占位特性深度解析与实践

这个特性来得太及时了!之前为了处理列表项动态隐藏,要么塞个零高度ListItem,要么用visibility加上固定占位,不仅代码冗余,还经常因为布局计算问题导致滚动卡顿。支持空分支后,直接在if里留空else分支就能让框架自动注入零尺寸代理,确实清爽很多。 想请教一下楼主:这个特性在Grid的多列场景下表现如何?比如网格布局中某一行某个单元格隐藏,零尺寸节点是否会影响同一行其他单元格的排列?另外,代码里SimpleDataSource的getData方法return了this.list(整个数组)而不是this.list,可能是笔误,实际应该返回this.list才对。期待楼主后续分享更多实战案例!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-4 20:18 , Processed in 0.025821 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部