鸿蒙开发实战案例:纵向横向列表联动案例
2025-04-11 本文已影响0人
迪士尼在逃程序员
介绍
本示例主要通过List组件绑定Scroller滚动控制器和LazyForEach数据懒加载来实现纵向横向列表联动,该场景多用于汽车参数对比,股票信息查看。
效果图预览

使用说明
- 纵向划动列表,内容和行标题保持联动
- 横向划动列表,内容和列标题保持联动
实现思路
本示例通过将每一个List绑定不同的Scroller对象,通过控制Scroller对象的滚动偏移量,使同一方向滚动的List的滚动量保持一致,实现横向纵向列表联动。
- 创建BasicDataSource类,LazyForEach加载数据。
- 顶部列表,底部左侧列表,底部右侧列表分别绑定不同的Scroller对象。声明一个变量,存储展示内容横向滚动的偏移量。
private topListScroller: Scroller = new Scroller(); // 顶部列表(列标题)的滚动控制器
private bottomLeftListScroller: Scroller = new Scroller(); // 底部列表左侧(行标题)的滚动控制器
private bottomRightListScroller: Scroller = new Scroller(); // 底部列表右侧(展示内容)的滚动控制器
private remainOffset: number = 0; // 每一行内容的滚动偏移量
- 通过对象保存Scroller数组,底部右侧每一行内容绑定一个Scroller对象。
loadShowData(): void {
const context: Context = getContext(this);
// 读取car.json里的数据
let jsonData: Uint8Array = context.resourceManager.getRawFileContentSync('car.json');
// 将数据解码,转成字符串
let stringData: string = util.TextDecoder.create('utf-8').decodeToString(jsonData);
let data: [] = JSON.parse(stringData) as [];
for (let i = 0; i < data.length; i++) {
const dataItem = data[i] as ShowData;
let showData: ShowData = new ShowData();
showData.sticky = dataItem.sticky;
showData.sub = dataItem.sub;
// 保存Scroller数组,与左侧标题一一对应
showData.sub.forEach(element => {
let scroller: Scroller = new Scroller();
showData.scrollerArray.push(scroller);
})
this.showDataArray.push(showData);
}
this.dataSource.setData(this.showDataArray);
}
- 顶部列表绑定topListScroller,列表横向划动时,让每一行的滚动控制器保持同步滚动,实现联动。
List({ scroller: this.topListScroller }) {
this.topListItem();
}
.edgeEffect(EdgeEffect.None) // 将边缘滑动效果设置为无
.listDirection(Axis.Horizontal) // 设置滚动方向为横向滚动
.scrollBar(BarState.Off)
.onScrollFrameBegin((offset: number, state: ScrollState) => {
// 顶部列标题列表滚动时,让每一行的滚动控制器保持同步滚动,实现联动
this.dataSource.getAllData().forEach((showData: ShowData) => {
showData.scrollerArray.forEach((scroller: Scroller) => {
// 偏移量为顶部列表滚动控制器当前偏移量+本次滚动的偏移量
scroller.scrollTo({ xOffset: this.topListScroller.currentOffset().xOffset + offset, yOffset: 0 });
})
})
return { offsetRemain: offset };
})
// ...
- 底部左侧列表绑定bottomLeftListScroller,列表纵向滚动时,通过控制bottomRightListScroller的滚动偏移量实现联动。
List({ scroller: this.bottomLeftListScroller }) {
// TODO:性能知识点:数据量较大,使用了[LazyForEach](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-rendering-control-lazyforeach-V5) 进行数据懒加载优化,以降低内存占用和渲染开销
LazyForEach(this.dataSource, (item: ShowData, index: number) => {
ListItemGroup({ header: this.leftFixedTitle(item.sticky) }) {
this.bottomLeftListItem(item);
}
}, (item: ShowData, index: number) => item.sticky + index)
}
.onScrollFrameBegin((offset: number, state: ScrollState) => {
// 通过控制右下列表的滚动控制器来保持和左下列表的联动
this.bottomRightListScroller.scrollTo({
xOffset: 0,
// 滚动偏移量为左下列表滚动控制器的当前偏移量+本次滚动的的偏移量
yOffset: this.bottomLeftListScroller.currentOffset().yOffset + offset
});
return { offsetRemain: offset };
})
// ...
- 底部右侧列表绑定bottomRightListScroller,列表纵向滚动时,通过控制bottomLeftListScroller的滚动偏移量实现纵向列表联动。列表纵向滚动时,让每一行的滚动控制器的滚动偏移量都保持一致,实现联动。通过父子传值(initOffset),使每一行展示内容的初始滚动偏移量保持一致。
List({ scroller: this.bottomRightListScroller }) {
// TODO:性能知识点:数据量较大,使用了[LazyForEach](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-rendering-control-lazyforeach-V5) 进行数据懒加载优化,以降低内存占用和渲染开销
LazyForEach(this.dataSource, (item: ShowData, index: number) => {
ListItemGroup({ header: this.rightFixedTitle(index) }) {
this.bottomRightListItem(item);
};
}, (item: ShowData, index: number) => item.sticky);
}
.onScrollFrameBegin((offset: number, state: ScrollState) => {
// 下方左侧行标题列表与下方右侧列表的滚动偏移量保持一致
this.bottomLeftListScroller.scrollTo({
xOffset: 0,
// 滚动偏移量为右下列表滚动控制器的当前偏移量+本次滚动的的偏移量
yOffset: this.bottomRightListScroller.currentOffset().yOffset + offset
})
return { offsetRemain: offset };
})
.onDidScroll((scrollOffset: number, scrollState: ScrollState) => {
// 行标题列表纵向滚动时,下方右侧的每一行展示内容的横向偏移量保持一致
this.dataSource.getAllData().forEach((showData: ShowData) => {
showData.scrollerArray.forEach((scroller: Scroller) => {
scroller.scrollTo({ xOffset: this.remainOffset, yOffset: 0 });
})
})
})
// ...
- 横向滚动列表每一行内容都绑定一个Scroller对象,列表滚动时,通过传参,传递滚动偏移量,使每一行内容的滚动偏移量都保持一致。
List({ scroller: this.scroller }) {
LazyForEach(this.dataSource, (item: string, index: number) => {
this.singleLineListItem(item);
}
}, (item: string) => item)
}
.onScrollFrameBegin((offset: number, scrollState: ScrollState) => {
if (this.scrollCallback) {
// 传递滚动偏移量
this.scrollCallback(this.scroller!.currentOffset().xOffset + offset);
}
return { offsetRemain: offset };
})
.onDidScroll((scrollOffset: number, scrollState: ScrollState) => {
if (this.remainOffsetCallback) {
// 更新滚动偏移量
this.remainOffsetCallback(this.scroller!.currentOffset().xOffset);
}
})
.edgeEffect(EdgeEffect.None) // 将边缘滑动效果设置为无
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)
// ...
高性能知识点
本示例数据量较少的列表,使用ForEach加载List数据,数据量较多的列表使用了LazyForEach进行数据懒加载。
本示例的滑动过程帧率可以达到满帧(120帧),trace截图如下所示:

工程结构&模块类型
verticalhorizontallinkage // har类型
|---datasource
| |---DataSource.ets // LazyForEach控制器
| |---ShowData.ets // 数据模型层-列表mock数据类型
|---view
| |---VerticalAndHorizontalList.ets // 视图层-应用主页面
写在最后
- 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习知识点,请移步前往小编:
https://gitee.com/MNxiaona/733GH/blob/master/jianshu
