精讲鸿蒙瀑布流搭载懒加载使用
瀑布流是应用开发中相当常见的开发场景。它通过容器自身的布局规则,将元素项目自上而下排列,在整体界面的呈现上,多列参差不齐、不停加载的形式使其内容看着像瀑布一样从上而下倾泻。借助其特点,瀑布流通常被用于展示图片资讯、购物商品、直播视频等多种形式的数据。当瀑布流上下滑动时,由于无限加载的特性,其能展示的数目非常多;大小不一的子元素,也带来了测量绘制的性能消耗。针对瀑布流这种场景进行性能优化,可以在加快
瀑布流搭载懒加载使用
概述
瀑布流是应用开发中相当常见的开发场景。它通过容器自身的布局规则,将元素项目自上而下排列,在整体界面的呈现上,多列参差不齐、不停加载的形式使其内容看着像瀑布一样从上而下倾泻。借助其特点,瀑布流通常被用于展示图片资讯、购物商品、直播视频等多种形式的数据。当瀑布流上下滑动时,由于无限加载的特性,其能展示的数目非常多;大小不一的子元素,也带来了测量绘制的性能消耗。针对瀑布流这种场景进行性能优化,可以在加快渲染速度、提升滑动帧率、降低内存占用等方面,增强应用的运行效率,进而提升用户的操作体验。
懒加载
先看一下组件示例代码中瀑布流的基本用法:build() {
Column({ space: 2 }) {
WaterFlow() {
LazyForEach(this.datasource, (item: number) => {
FlowItem() {
Column() {
Text("N" + item).fontSize(12).height('16')
Image('res/waterFlowTest (' + item % 5 + ').jpg')
.objectFit(ImageFit.Fill)
.width('100%')
.layoutWeight(1)
}
}
.width('100%')
.height(this.itemHeightArray[item])
.backgroundColor(this.colors[item % 5])
}, (item: string) => item)
}
.columnsTemplate("1fr 1fr")
.columnsGap(10)
.rowsGap(5)
.backgroundColor(0xFAEEE0)
.width('100%')
.height('80%')
}
}
示例代码已经使用了LazyForEach进行数据懒加载,WaterFlow布局时会根据可视区域按需创建FlowItem组件,并在FlowItem滑出可视区域外时销毁以降低内存占用。
瀑布流的开发,也属于长列表加载的一种场景,其LazyForEach懒加载原理及性能分析可参考:《长列表加载性能优化-懒加载》
缓存数据项
LazyForEach懒加载可以通过设置cachedCount来指定缓存数量,在设置cachedCount后,除屏幕内显示的Item组件外,还会预先将屏幕可视区外指定数量的数据缓存。这样当一个屏幕数据加载完成后,再次向下滑动时,会先加载上一次请求的数据,加载完成后,再加载本次请求的数据。详细原理及性能分析参考:《长列表加载性能优化-缓存列表项》
组件复用
考虑到滑动场景存在FlowItem及其子组件的频繁创建和销毁,可以将FlowItem中的组件封装成自定义组件,并使用@Reusable装饰器修饰,使其具备组件复用能力,减少ArkUI框架内部反复创建销毁节点的开销。另外,@Reusable装饰器实现的组件复用是限定在父组件内的,如果需要支持全局范围的组件复用,可使用全局组件复用池三方库。
组件复用详细原理及性能分析参考:《长列表加载性能优化-组件复用》
全局组件复用池三方库:nodepool
无限滑动
实际开发过程中,瀑布流布局里的数据一般不会一次性加载完全部数据,而是每次加载一定量的数据。这样的话,就需要在应用滑动时加载数据。同时,如果在滑动过程中就加载了新的数据,那么配合懒加载与缓存列表项的话,缓存中的数据在未使用完就能得到补充,因此用户滑动中就不会感觉到数据加载,从而实现无限滑动的效果(具体效果参见实践总结章节)。同时,由于提前加载了数据,因此不会在某一时刻存在大量组件需要创建渲染的情况,减少了因同一时间创建大量组件而导致丢帧的情况。对应场景下,需通过在一些事件中加载数据的方式来实现无限滑动。例如:瀑布流触底方法(onReachEnd)、瀑布流组件滑动index变化事件(onScrollIndex)。
- onReachEnd加载数据时,应用会在数据源中的数据全部渲染展示完了之后再加载新数据。这样就会有明显的加载动画出现,用户需等待新数据加载完成后才能再接着浏览瀑布流。
- onScrollIndex加载数据时,瀑布流组件滑动可视区index变化时执行任务,此时则需限制加载数据的条件(例:当前可视区最后一个组件的index + 20 === 数据源数量时),否则每个瀑布流组件滑动index变化时,都会加载数据。推荐在onScrollIndex里做操作,让瀑布流未到达底部时就开始加载数据,这样用户使用时就感知不到数据加载过程,从而提升体验。
相关流程如下:
瀑布流组件加载流程图

代码中使用的是onScrollIndex加载数据,示例如下:
WaterFlow({ footer: this.footStyle, scroller: this.waterFlowScroller }) {
LazyForEach(this.waterFlowListData.getData(), (item: WaterFlowData, index: number) => {
FlowItem() {
// ...
}
.height(item.waterFlowHead.height / item.waterFlowHead.width * this.waterFlowItemWidth +
this.getTitleHeight(item.waterFlowDescription.title))
.backgroundColor(Color.White)
// ...
}, (item: WaterFlowData) => {
return item.waterFlowDescription.index.toString()
});
}
.onScrollIndex((_first: number, last: number) => {
this.listenNetworkEvent();
if (this.waterFlowListData.dataSource.totalCount() < CommonConstants.WATER_FLOW_MAX_COUNT
&& last + 20 === this.waterFlowListData.dataSource.totalCount()) {
// 模拟数据,只有500条,所以此处限制pageNo小于25
this.pageNo = this.pageNo + 1 > 25 ? this.pageNo - 24 : this.pageNo + 1;
this.waterFlowListData.addData(CommonConstants.MOCK_INTERFACE_WATER_FLOW_FILE_NAME, this.pageNo,
this.pageSize);
}
this.listDataCount = this.waterFlowListData.dataSource.totalCount();
})
固定宽高
与长列表不同的,瀑布流布局中各个卡片的高度是不同的,这就对瀑布流布局绘制提出了新的挑战。瀑布流的卡片高度是由瀑布流卡片自适应瀑布流的宽度得到的,因此卡片的高度无法直接指定。这就会使卡片渲染以后得到的高度与占位符的高度不一致,从而造成卡片的闪烁效果。固定宽高后,就可以在UI描述时指定组件的宽高数值。如果组件本身的宽高是固定的,理论上来讲,该组件在布局阶段不会参与measure阶段,其节点中保存了对应的大小信息,当瀑布流内容较多时,由于避免了组件整体的测算过程,性能会带来一定的提升。
另外,由于Image组件默认异步加载,提前设定FlowItem的高度,可以避免图片加载成功后高度变化触发的瀑布流布局刷新。
详细内容请参考:利用布局边界减少布局计算
**图1 **瀑布流页面卡片宽高计算逻辑示意图

如上图所示,两列瀑布流卡片的宽度 = (屏幕宽度 - 2 * 组件外边距(margin) - 瀑布流组件内边距(gap))/ 2。
瀑布流卡片中图片的高度imageHeight = 图片原始高度 / 图片原始宽度 * 瀑布流卡片宽度。
瀑布流卡片中描述性的高度titleHeight根据标题长度不同需设置不同的高度,其计算逻辑如下代码所示:
getTitleHeight(title: string) {
let textWidth: number = MeasureText.measureText({
textContent: title,
fontSize: $r('app.float.font_size_14')
});
return textWidth > (this.waterFlowItemWidth - CommonConstants.DESCRIPTION_MARGIN_LEFT * 2) ?
CommonConstants.DESCRIPTION_THREE_LINES_HEIGHT : CommonConstants.DESCRIPTION_TWO_LINES_HEIGHT;
}
瀑布流卡片的高度 = imageHeight + titleHeight。
更多推荐


所有评论(0)