
15 — 滚动容器 Scroll 与定位控制一、引言滚动是列表类应用最核心的交互评论要滚到底部看全部、视频要上下滑动翻页、作品列表要滚到指定位置回看。HarmonyOS 中滚动能力由两类容器承担一类是Scroll通用滚动容器负责任意内容的滚动另一类是List、Grid、Swiper等专用滚动容器负责结构化数据的滚动与翻页。本文结合multi-short-video项目代码讲解滚动容器的使用、滚动定位控制与滚动状态监听并剖析评论列表与视频翻页中的工程实践。二、Scroll 组件与滚动条控制Scroll是一个可滚动的容器子组件可以是任意内容。基础用法如下通用示例Entry ComponentV2 struct ScrollDemo { private scroller: Scroller new Scroller(); build() { Column() { Button(滚到底部).onClick(() this.scroller.scrollEdge(Edge.Bottom)) Scroll(this.scroller) { Column() { ...超长内容... } } .scrollBar(BarState.Auto) // 滚动条自动显隐 .edgeEffect(EdgeEffect.Spring) // 回弹效果 .onScroll((x: number, y: number) { console.info(x: ${x}, y: ${y}); }) } } }Scroller是滚动控制器绑定到 Scroll 后即可编程控制滚动scrollTo(x, y)滚动到具体坐标、scrollEdge(Edge.Top/Bottom)直接跳转顶部或底部、scrollPage按页翻动。scrollBar支持Auto按需显隐、On常显、Off隐藏。需要说明的是multi-short-video项目中结构化数据滚动全部由专用容器承担评论列表用Listfeatures/multishortvideocomment/src/main/ets/view/Comment.ets、作品页用Gridfeatures/multishortvideoindividual/src/main/ets/view/Works.ets并统一通过scrollBar(BarState.Off)隐藏滚动条营造短视频产品无边框的沉浸观感。Scroll 适用于内容形态自由、需要精确坐标控制的场景两者并不冲突。三、滚动定位Scroller 与 SwiperController视频推荐流采用Swiper纵向翻页实现上下滑动切换视频features/multishortvideoadaptivevideo/src/main/ets/view/AdaptiveVideo.ets。Swiper 同样有控制器SwiperController与 Scroller 的定位思路一致只是语义是翻到第几页private swiperController: SwiperController new SwiperController(); Local curIndex: number 0; // 智慧屏遥控器方向键驱动翻页定位 .onKeyPreIme((event: KeyEvent) { if (event.type KeyType.Down) { if (event.keyCode KeyCode.KEYCODE_DPAD_DOWN) { this.swiperController.showNext(); // 定位到下一视频 } if (event.keyCode KeyCode.KEYCODE_DPAD_UP) { this.swiperController.showPrevious(); // 定位到上一视频 } } return false; })遥控器按下的瞬间即可精确翻页这正是滚动定位的价值——不用等用户手势程序主动控制滚动容器到达指定位置。showNext()/showPrevious()对应 Swiper 的相邻页showPage(index)可直达任意页动画时长由.duration(300)与.curve(Curve.Ease)控制翻页手感平滑。四、滚动状态监听与业务联动滚动状态监听是滚动结束即执行业务的关键。视频翻页完成的瞬间需要把播放目标切到新视频并重置播放进度AdaptiveVideo.ets用onAnimationStart捕获翻页起始Swiper(this.swiperController) { RepeatAvDataSourceModel(this.avDataSource) .each((item: RepeatItemAvDataSourceModel) { Stack() { AdaptiveAVPlayer({ currentIndex: this.curIndex, index: item.index, currentSource: item.item.currentSource, seekToTime: this.seekToTime, onStateNotify: (state: media.AVPlayerState) { this.currentState state; } }) ... } }) .key((item: AvDataSourceModel) JSON.stringify(item)) .virtualScroll({ totalCount: this.avDataSource.length }) } .cachedCount(2) // 预加载前后各 2 页保证滑动不断流 .vertical(true) .loop(false) .onAnimationStart((index: number, targetIndex: number) { this.curIndex targetIndex; // 记录目标页索引驱动播放器切换 this.currentTime 0; this.seekToTime -1; // 重置进度条状态 })curIndex通过Local声明变化后自动通知依赖它的AdaptiveAVPlayer切换播放源。cachedCount(2)是滚动性能的关键——它让 Swiper 预创建相邻页的播放器翻页时无需现场创建避免黑屏等待。五、评论列表的滚动加载与嵌套滚动协调评论列表的滚动加载在项目中表现为两种形态一是List virtualScroll的按需渲染Comment.ets配合末尾的没有更多了尾项表达数据边界List() { RepeatCommentDataModel(this.commentData) .each((item: RepeatItemCommentDataModel) { ListItem() { ...评论项... } }) .key((item: CommentDataModel) JSON.stringify(item)) .virtualScroll({ totalCount: this.commentData.length }) ListItem() { Row() { Text($r(app.string.nothing_more))... } .padding({ top: 20, bottom: 30 }) .margin({ bottom: 56 }) } } .scrollBar(BarState.Off)二是评论浮层与视频页的嵌套滚动协调。手机端评论通过bindSheet半模态弹出AdaptiveVideo.ets弹层内部是独立的 List 滚动与底层 Swiper 翻页互不干扰大屏端评论以SplitComment分栏形式侧开底层视频继续播放。实现这种协调的核心是滚动容器隔离半模态弹窗自带独立的滚动上下文底层 Swiper 在弹窗打开时自动停止手势接收开发者无需手动禁止底层滚动。唯一要注意的是弹窗内的输入框避让——评论输入框贴底定位RelativeContaineralignRules键盘弹出时输入框自动上移滚动列表随之收缩前文已述。六、总结与最佳实践按内容形态选容器自由内容用Scroll Scroller结构化数据用List/Grid/Swiper定位 API 语义不同但思路一致。主动定位用控制器Scroller.scrollTo/scrollEdge、SwiperController.showNext/showPrevious让程序精确控制滚动位置适配遥控器、键盘等非触摸输入。监听滚动状态驱动业务onAnimationStart等回调中同步更新索引与状态如curIndex由状态驱动播放器切换而不是直接操作视图。预加载保流畅视频列表用cachedCount(2)、评论用virtualScroll控制渲染范围以换流畅度。嵌套滚动靠隔离弹窗/分栏自带滚动上下文避免多层容器抢手势隐藏滚动条BarState.Off保持沉浸观感。