鸿蒙应用开发实战【18】— Scroll + ForEach 列表滚动实战
鸿蒙应用开发实战【18】— Scroll ForEach 列表滚动实战前言欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net列表展示是移动端最核心的 UI 模式之一。号码助手的首页、卡号管理页、状态列表页都大量使用了Scroll ForEach组合来展示可滚动列表。本篇深入讲解这个组合的使用方式、性能优化策略以及与List组件的区别。本篇涵盖Scroll 组件用法、ForEach 数据渲染、key 函数设计、layoutWeight 弹性高度、底部留空策略、Scroll Column 的组合、List vs Scroll 的选择、下拉刷新实现。图1Scroll ForEach 布局结构、layoutWeight 弹性高度、key 函数设计、底部留空策略一、Scroll 组件基础1.1 Scroll 与 List 的选择特性Scroll Column/RowList内容类型任意组件异构内容同类型列表项性能全量渲染适合 100 项虚拟化适合 100 项滚动方向横向/纵向纵向/横向需配置编程接口相对简单功能更丰富号码助手使用✅ 首页/卡号管理数量少未使用数据量小不需要1.2 Scroll 基本用法Scroll(){Column(){// 列表内容ForEach(this.items,(item:ItemType){ItemComponent({data:item})},(item:ItemType)${item.id})// 底部留空避免内容被 FAB/底栏遮挡Text().height(90)}.padding({left:16,right:16})}.layoutWeight(1)// 占用 Column 中的剩余垂直空间二、号码助手中的 Scroll 实现2.1 首页应用列表// HomePage.ets — 正常列表模式Scroll(){Column(){// ① ForEach 渲染过滤后的列表ForEach(this.getFilteredRows(),(row:AppRow){AppListItem({appName:row.binding.app_name,iconColor:row.binding.icon_key,category:row.binding.category,cardLabel:row.cardLabel,status:row.binding.status,bindingId:row.binding.id??0})},(row:AppRow)binding_${row.binding.id??0}_${row.binding.status})// ↑ key 函数确保 status 变化时正确重渲染// ② 底部留空避免最后一项被 FAB 悬浮按钮遮挡Text().height(90)}.padding({left:16,right:16})}.layoutWeight(1)// ← 关键弹性占满剩余高度2.2 批量选择模式列表// 批量选择模式带复选框的列表Scroll(){Column(){ForEach(this.getFilteredRows(),(row:AppRow){this.SelectableRow(row)// 调用 Builder 方法},(row:AppRow)sel_${row.binding.id??0}_${this.selectedIds.includes(row.binding.id??0)?1:0})// ↑ key 函数包含选中状态选中/取消选中时重渲染Text().height(80)// 批量操作底栏高度留空}.padding({left:16,right:16})}.layoutWeight(1)2.3 卡号管理列表// CardManagePage.etsScroll(){Column(){if(this.loading){LoadingProgress().width(40).height(40).color(AppColors.PRIMARY)}else{ForEach(this.cards,(card:CardEntity){this.CardRow(card)},(card:CardEntity)card_${card.id??0})}Text().height(90)}.padding({left:16,right:16,top:8})}.layoutWeight(1)三、ForEach 详解3.1 基本语法ForEach(arr:ArrayT,// 数据数组itemGenerator:(item:T,idx:number)void,// 渲染函数keyGenerator?:(item:T,idx:number)string// key 函数强烈建议)3.2 key 函数的重要性// key 函数决定 ArkUI 如何识别列表项的身份// ❌ 不提供 key每次数组变化时全量重渲染ForEach(this.rows,(row:AppRow){AppListItem({...})})// ❌ 只用 index 作为 key删除中间项时后续全部重渲染ForEach(this.rows,(row:AppRow){AppListItem({...})},(row:AppRow,idx:number)${idx})// ✅ 用业务 ID 关键状态精确最小化重渲染ForEach(this.rows,(row:AppRow){AppListItem({...})},(row:AppRow)binding_${row.binding.id??0}_${row.binding.status})3.3 key 函数设计原则唯一性同一数组内 key 必须唯一稳定性数据未变化时 key 不变包含影响渲染的状态如果 status 变化影响 UIkey 中要包含 status// 示例不同场景的 key 函数设计// 场景1只有增删无状态变化(row:AppRow)${row.binding.id??0}// 场景2有状态变化选中/未选中(row:AppRow)${row.binding.id??0}_${this.selectedIds.includes(row.binding.id??0)?sel:}// 场景3有多个维度影响渲染status 筛选条件(row:AppRow)binding_${row.binding.id??0}_${row.binding.status}_${this.filterCatIdx}_${this.filterCardIdx}四、layoutWeight 高度管理4.1 为什么 Scroll 需要 layoutWeight// 首页的布局结构Column(){// 标题栏固定高度TitleBar()// height: 44vp// 统计卡固定高度StatsRow()// height: ~88vp// 分区标题固定高度SectionHeader()// height: 44vp// 内容区需要占满剩余高度Scroll(){// ...}.layoutWeight(1)// ← 弹性分配剩余高度// 如果不加 layoutWeightScroll 高度为 0无法滚动}.height(100%)4.2 layoutWeight 的计算规则// Column 总高度 屏幕高度.height(100%)// 减去固定高度子组件后的剩余 Scroll 的高度// 例屏幕高度 800vp// 标题栏44vp// 统计卡88vp// 分区标题44vp// 剩余800 - 44 - 88 - 44 624vp → Scroll 的高度五、底部留空策略5.1 为什么需要底部留空// 问题FAB 悬浮按钮.overlay遮挡了最后一个列表项// 解决在列表最后添加一个空白占位Scroll(){Column(){ForEach(this.rows,...)// 底部占位高度 FAB 高度 底部安全区域Text().height(90)// FAB 约 48vp bottom: 24 余量 ≈ 90vp}}5.2 不同场景的留空高度场景建议留空高度有 FAB 悬浮按钮90vp有批量操作底栏~64vp80vp无底部遮挡20vp防止内容贴边六、Scroll 的其他配置6.1 横向滚动// 横向 Scroll如卡片横向滑动Scroll(){Row({space:12}){ForEach(this.cards,(card:CardEntity){CardThumbnail({data:card}).width(160)// 固定每张卡片宽度},(card:CardEntity)${card.id??0})}.padding({left:16,right:16})}.scrollable(ScrollDirection.Horizontal)// 横向滚动.scrollBar(BarState.Off)// 隐藏滚动条6.2 ScrollController 编程控制privatescroller:ScrollernewScroller()Scroll(this.scroller){Column(){...}}// 滚动到顶部Button(回到顶部).onClick((){this.scroller.scrollTo({xOffset:0,yOffset:0})})// 滚动到底部this.scroller.scrollEdge(Edge.Bottom)七、本篇小结知识点核心要点Scroll Column适合 100 项的列表全量渲染layoutWeight(1)让 Scroll 弹性占满剩余高度必须ForEach key 函数包含 id 影响渲染的状态精确重渲染底部留空Text(‘’).height(90) 避免 FAB 遮挡最后一项ScrollController编程控制滚动位置参考资料鸿蒙应用开发实战【05】— 首页开发与状态管理鸿蒙应用开发实战【79】— LazyForEach 虚拟化Scroll 组件参考ForEach 使用指南List 组件参考LazyForEach 高性能列表layoutWeight 属性Column 容器如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.netScroll 组件文档https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-scrollForEach 指南https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-foreach

相关新闻