WaterFlow:瀑布流容器,由“行”和“列”分割的单元格所组成,通过容器自身的排列规则,将不同大小的“项目”自上而下,如瀑布般紧密布局。瀑布流组件布局模式有从上到下的布局模式(ALWAYS_TOP_DOWN)和移动窗口式的布局模式(SLIDING_WINDOW)。
采用从上到下的布局模式时,视窗内的子组件依赖视窗上方所有子组件的布局信息,切换列数时需要计算上方所有子组件的布局信息,而采用移动窗口式的布局模式时,切换列数时只需要布局视窗内的子组件,对视窗上方的子组件没有依赖关系。在创建WaterFlow时可设置layoutMode参数来设置布局模式,默认是从上到下的布局模式。
WaterFlow组件加载较多数据,在切换列数时出现卡顿现象,则排查WaterFlow组件创建时是否设置layoutMode为WaterFlowLayoutMode.SLIDING_WINDOW,如下关键代码可看到未设置layoutMode参数,会导致在切换列数时会计算上方所有子组件的布局信息,组件数量过多时布局耗时多,会出现卡顿现象。
class WaterFlowDataSource implements IDataSource { private dataArray: number[] = []; private listeners: DataChangeListener[] = []; constructor() { for (let i = 0; i < 1000; i++) { this.dataArray.push(i); } } public getData(index: number): number { return this.dataArray[index]; } notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }); } public totalCount(): number { return this.dataArray.length; } registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { this.listeners.push(listener); } } unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { this.listeners.splice(pos, 1); } } public addItem(index: number): void { this.dataArray.splice(index, 0, this.dataArray.length); this.notifyDataAdd(index); } } @Reusable @Component struct ReusableFlowItem { item: number = 0; build() { Text(`${this.item}`) .fontWeight(FontWeight.Regular) .fontSize(20) .fontColor(Color.White) } } @Entry @Component export struct WaterFlowComponentPage { private list: WaterFlowDataSource = new WaterFlowDataSource(); @State column: number = 3; scroller: Scroller = new Scroller(); build() { Column() { WaterFlow({ scroller: this.scroller }) { LazyForEach(this.list, (item: number) => { FlowItem() { ReusableFlowItem({ item: item }) } .backgroundColor('#0A59F7') .border({ radius: 8 }) .width('100%') .height(120) }, (item: string, _index: number) => item.toString() + _index) } .cachedCount(2) .width('300vp') .height('200vp') .layoutDirection(FlexDirection.Column) .friction(0.75) .columnsTemplate('1fr '.repeat(this.column)) .rowsTemplate('1fr 1fr 1fr') .columnsGap(6) .rowsGap(6) .padding(2) .border({ width: 1, color: '#0A59F7', radius: 12 }) .onAppear(() => { // 滚动到底部 setTimeout(() => { this.scroller.scrollEdge(Edge.End); }, 1000); }) Slider({ value: this.column, min: 1, max: 4 }).onChange((value: number) => { // 切换显示的列数 this.column = value; }) } .width('100%') } }
将layoutMode设置成WaterFlowLayoutMode.SLIDING_WINDOW,采用移动窗口式的布局模式。
class WaterFlowDataSource implements IDataSource { private dataArray: number[] = []; private listeners: DataChangeListener[] = []; constructor() { for (let i = 0; i < 1000; i++) { this.dataArray.push(i); } } public getData(index: number): number { return this.dataArray[index]; } notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }); } public totalCount(): number { return this.dataArray.length; } registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { this.listeners.push(listener); } } unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { this.listeners.splice(pos, 1); } } public addItem(index: number): void { this.dataArray.splice(index, 0, this.dataArray.length); this.notifyDataAdd(index); } } @Reusable @Component struct ReusableFlowItem { item: number = 0; build() { Text(`${this.item}`) .fontWeight(FontWeight.Regular) .fontSize(20) .fontColor(Color.White) } } @Entry @Component export struct WaterFlowComponentPage { private list: WaterFlowDataSource = new WaterFlowDataSource(); @State column: number = 3; scroller: Scroller = new Scroller(); build() { Column() { WaterFlow({ scroller: this.scroller, layoutMode: WaterFlowLayoutMode.SLIDING_WINDOW }) { LazyForEach(this.list, (item: number) => { FlowItem() { ReusableFlowItem({ item: item }) } .backgroundColor('#0A59F7') .border({ radius: 8 }) .width('100%') .height(120) }, (item: string, _index: number) => item.toString() + _index) } .cachedCount(2) .width('300vp') .height('200vp') .layoutDirection(FlexDirection.Column) .friction(0.75) .columnsTemplate('1fr '.repeat(this.column)) .rowsTemplate('1fr 1fr 1fr') .columnsGap(6) .rowsGap(6) .padding(2) .border({ width: 1, color: '#0A59F7', radius: 12 }) .onAppear(() => { // 滚动到底部 setTimeout(() => { this.scroller.scrollEdge(Edge.End); }, 1000); }) Slider({ value: this.column, min: 1, max: 4 }).onChange((value: number) => { // 切换显示的列数 this.column = value; }) } .width('100%') } }