【共创季稿事节】鸿蒙原生 ArkTS 布局方式之 Scroll 可滚动容器入门:何时需要 Scroll
鸿蒙原生 ArkTS 布局方式之 Scroll 可滚动容器入门何时需要 Scroll一、前言在鸿蒙应用开发中内容溢出是最常见的布局问题之一。当页面内容总尺寸超过容器可视区域时开发者必须决定超出部分怎么办鸿蒙 ArkTS 给出了优雅的答案——Scroll 可滚动容器组件。本文通过一个完整的可运行示例带你深入理解 Scroll 容器的使用场景、核心属性和最佳实践。无论你是初学 HarmonyOS 还是从其他平台迁移本文都将帮你建立对 Scroll 的系统认知。二、为什么需要 Scroll 容器2.1 内容溢出的本质假设页面有一个高度固定的Column内部循环渲染了 20 个卡片每个高 52vp 间距 6vp总高度约 1154 vp。而容器的固定高度只有300vp。在这 300vp 里只能放下约 5 个卡片剩下的15 个被裁剪了——用户完全看不到它们。这就是内容溢出问题最直观的表现不是因为数据丢失而是因为布局容器没有提供滚动能力。2.2 没有 Scroll 的后果在鸿蒙 ArkTS 中Column和Row不会自动滚动Flex容器不会自动滚动超出父容器尺寸的内容默认被clip裁剪这意味着一旦内容超出屏幕高度就会被吃掉。2.3 Scroll 容器的解决方案Scroll是最基础的可滚动容器。它的核心能力包裹子组件通常是一个 Column 或 Row子组件内容超出自身尺寸时自动启用滚动交互用户通过手指滑动或鼠标滚轮查看全部内容一句话总结当内容可能超出容器时在外面包一层 Scroll。三、Scroll 容器核心概念API 243.1 基本用法Scroll() { Column() { Text(内容1); Text(内容2); // 更多内容... } .width(100%) } .width(100%) .height(300) .scrollable(ScrollDirection.Vertical) .scrollBar(BarState.Auto)3.2 核心属性详解1scrollable —— 设置滚动方向枚举值说明ScrollDirection.Vertical纵向滚动最常用ScrollDirection.Horizontal横向滚动ScrollDirection.None禁止滚动2scrollBar —— 控制滚动条枚举值说明BarState.Auto滚动时显示不滚动时隐藏BarState.On始终显示BarState.Off始终隐藏3edgeEffect —— 边缘效果枚举值说明EdgeEffect.Spring弹性回弹推荐EdgeEffect.Fade边缘渐隐EdgeEffect.None无效果3.3 事件监听Scroll() { /* ... */ } .onScroll((x, y) { /* 滚动偏移量回调 */ }) .onScrollEnd(() { /* 滚动结束 */ }) .onReachStart(() { /* 到达顶部 */ }) .onReachEnd(() { /* 到达底部 */ })3.4 程序化滚动控制private scroller: Scroller new Scroller(); Scroll(this.scroller) { /* ... */ } // 滚动控制方法 this.scroller.scrollTo({ xOffset: 0, yOffset: 200 }); this.scroller.scrollEdge(Edge.Top); this.scroller.scrollEdge(Edge.Bottom); this.scroller.scrollPage({ next: true });四、示例应用详解4.1 整体架构示例采用Tab 切换对比设计同一个位置展示无 Scroll和有 Scroll两种状态通过点击 Tab 切换直观对比效果差异。┌─ 标题 ──────────────────────────────┐ │ Scroll 可滚动容器入门 │ ├─ 说明区域 ───────────────────────────┤ │ 【布局要点】Scroll 的基本概念…… │ ├─ Tab 切换 ───────────────────────────┤ │ [❌无Scroll] [✅有Scroll] │ ├─ 演示区域 ───────────────────────────┤ │ ⚠️/✅ 状态提示条 │ │ ① Scroll 基础用法 │ │ ② 内容溢出问题 │ │ ……更多卡片…… │ └──────────────────────────────────────┘4.2 关键技术设计Tab 切换逻辑State selectedTab: number 0; Stack() { if (this.selectedTab 0) { this.NonScrollView(); } else { this.ScrollView(); } }点击 Tab 时更新selectedTabStack保持两个视图位置一致切换无缝。无 Scroll 版本Flex() { Column({ space: 6 }) { ForEach(this.GenerateItems(), (item: CardItem) { this.CardItemView(item) }, (item: CardItem) item.id.toString()) } } .height(300) // 固定高度——故意限制 .clip(true) // 超出裁剪关键300vp 高度只够显示约 5 个卡片剩余 15 个被裁剪。有 Scroll 版本Scroll() { Column({ space: 6 }) { ForEach(this.GenerateItems(), (item: CardItem) { this.CardItemView(item) }, (item: CardItem) item.id.toString()) } } .height(300) .scrollable(ScrollDirection.Vertical) .scrollBar(BarState.Auto) .edgeEffect(EdgeEffect.Spring)改动仅两点外层从Flex换成Scroll去掉.clip(true)。效果迥异。Builder 拆分示例将 UI 拆分为多个Builder函数TitleSection、DescriptionSection、TabBar、NonScrollView、ScrollView、CardItemView。每个函数职责单一两个对比视图共享相同的卡片渲染逻辑。数据生成GenerateItems()生成 20 个卡片每个包含 id、title、desc、color。20 个标题覆盖 Scroll 的不同知识点——从基础用法到性能优化每个卡片本身就是一个知识点。五、运行效果首页简洁入口页面展示Scroll 可滚动容器卡片点击导航到演示页。Tab 0「无 Scroll」红色提示条⚠️ Column 内容超出容器高度底部被裁剪。只显示前 5 个卡片其余不可见无任何滚动响应。Tab 1「有 Scroll」绿色提示条✅ Scroll 包裹 Column滑动可查看所有内容。右侧出现滚动条手指滑动可流畅查看所有 20 个卡片顶部/底部有弹性回弹效果。六、Scroll 使用场景指南6.1 何时用 Scroll场景需要 Scroll说明固定高度的 Column 内容不确定✅内容多时自动滚动全屏页面内容刚好一屏❌无溢出全屏页面内容可能超出一屏✅最常见场景Tab 内容页✅各 Tab 长度不同横向卡片轮播✅用 Horizontal 方向文章阅读页✅内容长度不确定设置页/配置列表✅选项可能很多6.2 Scroll vs 其他滚动方案方案适用场景特点Scroll内容量中等 50 项轻量灵活任意组件混排List超长列表50 项LazyForEach 懒加载性能最优Grid网格布局适合相册、商品展示Swiper轮播翻页一次一屏支持自动轮播Tabs多 Tab 页签结合 TabBar 使用选型建议内容量 50 项用Scroll≥ 50 项用ListLazyForEach网格用Grid。七、开发注意事项API 247.1 必须设置高度/宽度Scroll 必须有明确的高度纵向或宽度横向否则滚动无效。// ✅ 正确 Scroll() { /* ... */ }.height(300) Scroll() { /* ... */ }.layoutWeight(1) // ❌ 错误无高度约束内容无限延伸 Scroll() { /* ... */ }7.2 避免嵌套冲突垂直方向的 Scroll 不要直接嵌套// ❌ 错误 Scroll() { Scroll() { /* ... */ }.scrollable(ScrollDirection.Vertical) } .scrollable(ScrollDirection.Vertical)手势冲突体验差。如需嵌套使用不同方向或NestedScroll。7.3 Scroller 生命周期struct MyPage { private scroller: Scroller new Scroller(); // 不要在 Builder 中重复创建 }7.4 性能考量Scroll 一次性渲染所有子组件。对超长列表数百项改用ListLazyForEach实现虚拟化渲染。八、横向滚动示例Scroll() { Row({ space: 12 }) { ForEach(items, (item) { Text(item.title).fontSize(16) }) } .padding(16) } .scrollable(ScrollDirection.Horizontal) .scrollBar(BarState.Auto) .edgeEffect(EdgeEffect.Spring) .height(100)只需将scrollable设为Horizontal内部容器改用Row。九、Scroll 与 List 的取舍维度ScrollList渲染一次性全部渲染懒加载仅渲染可见项数据量 50 项50 项子组件任意组件混排通常同类型内置功能仅滚动选中、滑动删除、拖拽排序复杂度低中一句话内容少且结构灵活用 Scroll超长列表用 List。十、完整代码概览10.1 演示页面入口Entry Component struct ScrollDemo { State itemCount: number 20; State selectedTab: number 0; build() { Column() { this.TitleSection(); this.DescriptionSection(); this.TabBar(); Stack() { if (this.selectedTab 0) { this.NonScrollView(); } else { this.ScrollView(); } } .layoutWeight(1) } .height(100%) .width(100%) .backgroundColor(#F5F5F5) } // ... Builder 方法 }10.2 路由注册{src:[pages/Index,pages/ScrollDemo]}完整源码详见项目中的ScrollDemo.ets和Index.ets。十一、实战练习练习 1横向滚动专辑展示——Scroll Row10 个 120×120 卡片左右滑动。练习 2滚动控制按钮——使用Scroller添加回到顶部和跳到底部按钮。练习 3滚动驱动动画——监听onScroll根据yOffset动态改变标题栏透明度。练习 4嵌套滚动——创建搜索栏 水平 Tab 垂直内容列表的复合页面。十二、常见问题 FAQQ1Scroll 不滚动怎么办检查①是否设置固定高度/layoutWeight②内容是否超过容器尺寸③scrollable 是否为 None④有无手势冲突。Q2滚动条太难看怎么改可用scrollBarWidth设宽度或BarState.Off隐藏原生条自定义模拟滚动条。Q3如何监听滚动偏移量Scroll() { /* ... */ } .onScroll((x, y) console.info(已滚动${y}vp))Q4Scroll 中点击事件不生效检查onClick是否被手势事件拦截尝试.hitTestBehavior(HitTestMode.Default)。Q5API 24 和 API 23 区别核心 API 一致API 24 增强 NestedScroll 支持和性能优化代码完全兼容。十三、总结本文从内容溢出问题出发介绍了鸿蒙 ArkTS 中 Scroll 容器的核心概念、使用方法、属性和最佳实践。核心要点Scroll 是最基础的可滚动容器内容超出时外层包一层 Scroll 即可。三个核心属性scrollable方向、scrollBar滚动条、edgeEffect边缘效果。必须设置高度/宽度否则滚动无效。对比展示是最直观的教学方式——无 Scroll vs 有 Scroll 一目了然。中小量内容用 Scroll超长列表用 List。Scroll 是构建任何非平凡页面都绕不开的基础组件。掌握了它你就掌握了鸿蒙布局系统的溢出解决方案。参考资料HarmonyOS NEXT API 24 官方文档 · 示例源码entry/src/main/ets/pages/ScrollDemo.ets本文发布于 2026 年 6 月基于 HarmonyOS NEXT API 24已在 DevEco Studio 验证。