HarmonyOS 鸿蒙Next 求助 请大佬补充完整我的瀑布流

HarmonyOS 鸿蒙Next 求助 请大佬补充完整我的瀑布流 一开始利用WaterFlow容器写了个瀑布流,无奈瀑布流的滚动条和页面Scroll滚动冲突,于是乎想自己是实现一个瀑布流效果

思路大致如下:

  1. 分成两列,(两个Column),不给固定高度,让子元素自行把父容器撑大
  2. 每次添加数据时,找到相比较当前的2个容器高度‘矮’的那一个,(Height小的),往里塞数据
  3. 每次添加5条数据,那就进行了5次比大小,每次都找到小的那一个,理想情况就是左边aClomun添加一条,右边bCloumn添加一条,如此反复进行5次

说干就干,先整两个并排的Column出来,分别绑定空数组arr1和arr2,arr1添加数据,则左边的Cloumn添加数据,反则右边添加,

利用onAreaChange组件区域变化事件,来找出他们变化后的高度,进行比较

大致逻辑如下:

每点击一次按钮,就进行一次判断大小,并在小的里添加一条数据

初始情况:(给左边a默认塞一条数据,让他俩初始就不一高,方便用于比较)

点击第一次效果:(左右a有数据,右边b没数据,a高度肯定大于b,所以往右边添加数据)

点击第二次效果:(因为右边还比左边的高度小,所以依然在右边添加数据,没毛病)

点击第三次效果:(这时右边的2条加起来高度比左边1条要高,所以第三次往左边添加)

以上看似已经实现想要的效果了,只需要每次添加添加N条数据即可(循环添加,每次添加前都会比较大小)

这时我们给他加个for循环,让他一次添加5条数据进来,(把上面代码的for循环注释 // 去掉即可)

但这时候问题就出现了,点击后,视图直接在右边加了5条,左边还是初始那1条,相当于每次比较大小,都是fasle(右边高度小)

然后得出问题,onAreaChange事件似乎只有在for循环全部走完后,才会更新视图并出发onAreaChange改变,之后试了async awite ,定时器异步来操作也是不行

有没有大佬告诉我,想要完整实现我这个Demo,在一次添加5条数据时,让他每次都正确判断当前column高度后再添加数据,应该怎么搞啊??

下面是完整代码:

interface imgItem{
  key:number
  img:Resource
  title:string
  price:string
  sold:string
}

@Entry
@Component
export default struct FlexContent{
  scroller: Scroller = new Scroller()

  // 定义两个变量用于存储他们当前的高度
  @State aa:number = 0
  @State bb:number = 0
  // 定义两个数组arr1和arr2,首先给arr1塞条数据,让aColumn初始就比b高,好用于比较
  @State arr1:Array<imgItem> = [
    {
      key:1,
      img: $r('app.media.ic_drive_1'),
      title:"测试文字测试文字测试文字测试文字测试文字测试文字测试文字测试文字测试文字测试文字测试文字",
      price:'198',
      sold:'3000'
    }
  ]
  @State arr2:Array<imgItem> = []

  // RecommendItem为自定义的图文组件,为了实现瀑布流效果,每条数据的图片高度不一样,文字多少也不一样
  @Builder RecommendItem(img:Resource,title,price,sold){
    Column(){
      Image(img).width('100%').objectFit(ImageFit.Contain).objectRepeat(ImageRepeat.NoRepeat).borderRadius(10)
      Text(title).width('100%').padding(10).fontWeight(FontWeight.Bold)
      Row(){
        Text('¥').fontSize(14).fontColor('red').fontWeight(FontWeight.Bold)
        Text(price).fontSize(18).fontColor('red').fontWeight(FontWeight.Bold)
      }.width('100%').padding({left:10}).justifyContent(FlexAlign.Start).alignItems(VerticalAlign.Bottom)
      Text('已售 '+sold +'+').fontSize(12).fontColor('#999').padding({top:10,left:10}).width('100%').textAlign(TextAlign.Start)
    }.backgroundColor('#fff').padding({bottom:10})
  }

  build(){

    Scroll(){
      Stack({alignContent: Alignment.Top}){
        Column(){
          Text(this.aa.toString()).fontSize(30).backgroundColor('red').fontColor('#fff')
          Text(this.bb.toString()).fontSize(30).backgroundColor('blue').fontColor('#fff')
        }

        Row(){
          Column(){
            ForEach(this.arr1,(item:imgItem)=>{
              this.RecommendItem(item.img,item.title,item.price,item.sold)
            },(item:imgItem)=>JSON.stringify(item))
          }.onAreaChange((oldValue: Area, newValue: Area)=>{
            // 获取左边列的高度用aa记录
            this.aa = parseInt(newValue.height.toString())
          }).width('48%').border({width:1,color:'red'})
          Column(){
            ForEach(this.arr2,(item:imgItem)=>{
              this.RecommendItem(item.img,item.title,item.price,item.sold)
            },(item:imgItem)=>JSON.stringify(item))
          }.onAreaChange((oldValue: Area, newValue: Area)=>{
            // 获取右边列的高度用bb记录
            this.bb = parseInt(newValue.height.toString())
          }).width('50%').border({width:1,color:'blue'})
        }.margin({top:100}).alignItems(VerticalAlign.Top)

        Button('+++++ 添加数据 +++++').onClick(()=>{
          for(let i=0;i<5;i++){
            if(this.aa<this.bb){
              this.arr1.push(
                {
                  key:(this.arr1.length+1),
                  img: $r('app.media.ic_drive_1'),
                  title:"测试文字测试文字测试文字测试文字测试文字测试文字测试文字测试文字测试文字测试文字测试文字",
                  price:'39.9',
                  sold:'1万'
                }
              )
            } else {
              this.arr2.push(
                {
                  key:(this.arr2.length+1),
                  img: $r('app.media.ic_drive_2'),
                  title:"海狸先生黄鱼酥即食香酥小黄花鱼零食即食鱼仔小鱼干",
                  price:'39.9',
                  sold:'1万'
                }
              )
            }
          }
        }).margin({top:80})
      }
    }
  }
}

更多关于HarmonyOS 鸿蒙Next 求助 请大佬补充完整我的瀑布流的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

我建议你还是别自己写瀑布流组件了,在 API 10 中新增了

onScrollFrameBegin(event: (offset: number, state: ScrollState) => { offsetRemain })

HarmonyOS Next 在今年1月18日开发布会了。

你到时候用 onScrollFrameBegin 实现滚动嵌套的问题即可。

滚动嵌套问题可参考 Scroll 组件的示例二,也就是 Scroll 嵌套 List 的那个例子,到时候你改成 Scroll 嵌套 WaterFlow 即可。

更多关于HarmonyOS 鸿蒙Next 求助 请大佬补充完整我的瀑布流的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS(鸿蒙Next)中,实现瀑布流布局可以通过GridLayoutFlexLayout来完成。以下是一个简单的示例,展示如何使用GridLayout实现瀑布流布局。

import { GridLayout, GridItem, Column, Row } from '@ohos/ui';

export default class WaterfallFlow extends Component {
  render() {
    return (
      <GridLayout columns="repeat(auto-fill, minmax(150px, 1fr))" gap="10px">
        <GridItem>
          <Column>
            <Row>Item 1</Row>
            <Row>Item 2</Row>
          </Column>
        </GridItem>
        <GridItem>
          <Column>
            <Row>Item 3</Row>
            <Row>Item 4</Row>
          </Column>
        </GridItem>
        <GridItem>
          <Column>
            <Row>Item 5</Row>
            <Row>Item 6</Row>
          </Column>
        </GridItem>
        {/* 更多 GridItem */}
      </GridLayout>
    );
  }
}

在这个示例中,GridLayout用于创建网格布局,columns属性定义了列的宽度和数量,gap属性定义了网格之间的间距。每个GridItem包含一个ColumnColumn中可以放置多个Row,从而实现瀑布流的效果。

如果需要更复杂的布局,可以使用FlexLayout结合ColumnRow来实现动态调整的瀑布流布局。例如:

import { FlexLayout, Column, Row } from '@ohos/ui';

export default class WaterfallFlow extends Component {
  render() {
    return (
      <FlexLayout direction="column" wrap="wrap" gap="10px">
        <Column>
          <Row>Item 1</Row>
          <Row>Item 2</Row>
        </Column>
        <Column>
          <Row>Item 3</Row>
          <Row>Item 4</Row>
        </Column>
        <Column>
          <Row>Item 5</Row>
          <Row>Item 6</Row>
        </Column>
        {/* 更多 Column */}
      </FlexLayout>
    );
  }
}

在这个示例中,FlexLayout用于创建一个灵活的布局,direction属性设置为columnwrap属性设置为wrap,使得内容可以自动换行,从而实现瀑布流的效果。

回到顶部