鸿蒙开发之瀑布流&宫格布局

        瀑布流布局适用于卡片式结构。折叠态的单列卡片,到展开态显示双列卡片,不同高度的卡片形成错落有致的瀑布流布局。可以有效提升页面的浏览效率和可视化效果。

        瀑布流容器,由“行”和“列”分割的单元格所组成,通过容器自身的排列规则,将不同大小的“项目”自上而下,如瀑布般紧密布局。

说明

该组件从API Version 9开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。

子组件

包含FlowItem子组件。

说明

WaterFlow子组件的visibility属性设置为None时不显示,但依然会占用子组件对应的网格。

接口

WaterFlow(options?: {footer?: CustomBuilder, scroller?: Scroller})

参数:

参数名

参数类型

必填

参数描述

footer

CustomBuilder

设置WaterFlow尾部组件。

scroller

Scroller

可滚动组件的控制器,与可滚动组件绑定。

目前瀑布流仅支持Scroller组件的scrollToIndex接口。

属性

除支持通用属性外,还支持以下属性:

名称

参数类型

描述

columnsTemplate

string

设置当前瀑布流组件布局列的数量,不设置时默认1列。

例如, '1fr 1fr 2fr' 是将父组件分3列,将父组件允许的宽分为4等份,第一列占1份,第二列占1份,第三列占2份。并支持auto-fill

默认值:'1fr'

rowsTemplate

string

设置当前瀑布流组件布局行的数量,不设置时默认1行。

例如, '1fr 1fr 2fr'是将父组件分三行,将父组件允许的高分为4等份,第一行占1份,第二行占一份,第三行占2份。并支持auto-fill

默认值:'1fr'

itemConstraintSize

ConstraintSizeOptions

设置约束尺寸,子组件布局时,进行尺寸范围限制。

columnsGap

Length

设置列与列的间距。

默认值:0

rowsGap

Length

设置行与行的间距。

默认值:0

layoutDirection

FlexDirection

设置布局的主轴方向。

默认值:FlexDirection.Column

layoutDirection优先级高于rowsTemplate和columnsTemplate。根据layoutDirection设置情况,分为以下三种设置模式:

  • layoutDirection设置纵向布局(FlexDirection.Column 或 FlexDirection.ColumnReverse)

    此时columnsTemplate有效(如果未设置,取默认值)。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件纵向布局,辅轴均分成横向2列。

  • layoutDirection设置横向布局(FlexDirection.Row 或 FlexDirection.RowReverse)

    此时rowsTemplate有效(如果未设置,取默认值)。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件横向布局,辅轴均分成纵向3列。

  • layoutDirection未设置布局方向

    布局方向为layoutDirection的默认值:FlexDirection.Column,此时columnsTemplate有效。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件纵向布局,辅轴均分成横向2列。

事件

除支持通用事件外,还支持以下事件:

名称

功能描述

onReachStart(event: () => void)

瀑布流组件到达起始位置时触发。

onReachEnd(event: () => void)

瀑布流组件到底末尾位置时触发。

auto-fill说明

WaterFlow的columnsTemplate、rowsTemplate属性的auto-fill仅支持以下格式:

 
  1. repeat(auto-fill, track-size)

其中repeat、auto-fill为关键字。track-size为行高或者列宽,支持的单位包括px、vp、%或有效数字,track-size至少包括一个有效行高或者列宽。

示例

  @State minSize: number = 50
  @State maxSize: number = 100
  @State fontSize: number = 24
  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]
  scroller: Scroller = new Scroller()
  datasource: WaterFlowDataSource = new WaterFlowDataSource()
  private itemWidthArray: number[] = []
  private itemHeightArray: number[] = []


  // 计算flow item宽/高
  getSize() {
    let ret = Math.floor(Math.random() * this.maxSize)
    return (ret > this.minSize ? ret : this.minSize)
  }

  // 保存flow item宽/高
  getItemSizeArray() {
    for (let i = 0; i < 100; i++) {
      this.itemWidthArray.push(this.getSize())
      this.itemHeightArray.push(this.getSize())
    }
  }

  aboutToAppear() {
    this.getItemSizeArray()
  }

 /**
   * 商城数据
   */
  @Builder
  AppStoreData() {
    Column({ space: 2 }) {
      WaterFlow({ footer: this.itemFoot.bind(this), scroller: this.scroller }) {
        LazyForEach(this.datasource, (item: number) => {
          FlowItem() {
            Column() {
              Text("N" + item).fontSize(12).height('16')
              Image($r('app.media.home_otp_center'))
                .objectFit(ImageFit.Fill)
            }
          }
          .width(this.itemWidthArray[item])
          .height(this.itemHeightArray[item])
          .backgroundColor(this.colors[item % 5])
        }, item => item)
      }
      .columnsTemplate("1fr 1fr 1fr 1fr")
      .itemConstraintSize({
        minWidth: 0,
        maxWidth: '100%',
        minHeight: 0,
        maxHeight: '100%'
      })
      .columnsGap(10)
      .rowsGap(5)
      .onReachStart(() => {
        console.info("onReachStart")
      })
      .onReachEnd(() => {
        console.info("onReachEnd")
      })
      .backgroundColor(0xFAEEE0)
      .width('100%')
      .height('80%')
      .layoutDirection(FlexDirection.Column)
    }
  }
WaterFlowDataSource
/**
 * 瀑布流数据源
 */
export class WaterFlowDataSource implements IDataSource {
  private dataArray: number[] = []
  private listeners: DataChangeListener[] = []

  /**
   * 注销数据监听
   * @param listener
   */
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener)
    if (pos >= 0) {
      this.listeners.splice(pos, 1)
    }
  }

  /**
   * 注册数据监听
   * @param listener
   */
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener)
    }
  }

  /**
   * 获取数据
   * @param index
   * @returns
   */
  getData(index: number) {
    return this.dataArray[index]
  }

  /**
   * 获取数据总量
   * @returns
   */
  totalCount(): number {
    return this.dataArray.length
  }

  // 增加数据
  public Add1stItem(): void {
    this.dataArray.splice(0, 0, this.dataArray.length)
    this.notifyDataAdd(0)
  }

  // 在数据尾部增加一个元素
  public AddLastItem(): void {
    this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length)
    this.notifyDataAdd(this.dataArray.length-1)
  }

  // 在指定索引位置增加一个元素
  public AddItem(index: number): void {
    this.dataArray.splice(index, 0, this.dataArray.length)
    this.notifyDataAdd(index)
  }

  // 删除第一个元素
  public Delete1stItem(): void {
    this.dataArray.splice(0, 1)
    this.notifyDataDelete(0)
  }

  // 删除第二个元素
  public Delete2ndItem(): void {
    this.dataArray.splice(1, 1)
    this.notifyDataDelete(1)
  }

  // 删除最后一个元素
  public DeleteLastItem(): void {
    this.dataArray.splice(-1, 1)
    this.notifyDataDelete(this.dataArray.length)
  }

  // 重新加载数据
  public Reload(): void {
    this.dataArray.splice(1, 1)
    this.dataArray.splice(3, 2)
    this.notifyDataReload()
  }

  // 通知控制器数据变化
  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChanged(index)
    })
  }

  // 通知控制器数据删除
  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDeleted(index)
    })
  }

  // 通知控制器数据重新加载
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded()
    })
  }

  // 通知控制器数据增加
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdded(index)
    })
  }


}

        

  • 22
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: Android 六宫格布局是指将界面分为六个等大小的方格,每个方格中可以放置不同的控件或者视图。这种布局方式在Android应用的界面设计中经常使用,可以使界面看起来整齐、美观,并且提供了较高的灵活性。 实现六宫格布局的方法有很多种,其中比较简单的方式是使用GridLayout布局管理器。GridLayout可以将子视图按照行和列的方式进行排列,因此非常适合用于六宫格的界面设计。 在XML布局文件中,我们可以通过设置GridLayout的属性来实现六宫格布局。首先,我们需要将GridLayout设置为6行1列,表示界面将被分为六个水平方向的等高行;然后,我们可以在每个格子中添加其他的控件或者视图。通过设置每个格子的权重、行列位置等属性,可以实现不同的布局效果,例如让某些格子占据更多的空间或者选择合适的控件来填充格子。 另外,我们还可以通过Java代码来实现六宫格布局。可以使用GridLayoutManager或者自定义布局管理器继承自RecyclerView.LayoutManager来实现。这种方式可以更加灵活地控制子视图的排列方式,可以根据自己的需求定制不同的布局效果。 总之,Android六宫格布局是一种常见且实用的界面布局方式,可以通过使用GridLayout或者自定义布局管理器来实现。这种布局方式可以使界面整齐、美观,并且提供了较高的灵活性,适合用于不同类型的Android应用界面设计。 ### 回答2: 安卓的六宫格布局是一种常见的应用界面布局方式,它将屏幕分割为2行3列的六个等大的格子,每个格子可以放置不同的应用模块或者功能模块。 此布局通常用于主屏幕或者应用程序的菜单界面,以提供快速访问和导航。每个格子可以自定义放置不同的应用图标、小部件或者快捷方式,以满足用户的个性化需求。 六宫格布局的优势在于简单直观,用户可以一目了然地找到和使用所需的应用或者功能。同时,由于每个格子的尺寸相同,不同的应用图标或者模块之间的界面一致性很高,提升了用户界面的美观度和易用性。 此外,六宫格布局还可以根据用户的喜好进行调整和定制。用户可以自由地拖动和排列格子的位置,以适应个人喜好和使用习惯。这种灵活性使得用户可以根据自己的需求将常用的应用设置为更加方便的位置,提高了操作效率。 总的来说,安卓的六宫格布局提供了一种简单直观且易于个性化的界面布局方式,使得用户可以快速访问和导航不同的应用或者功能模块。它为用户提供了良好的用户体验和操作效率,受到广大安卓用户的喜爱。 ### 回答3: 六宫格布局是一种常见的Android布局方式,适用于需要将界面划分为6个等宽、等高的方格的情况。 在Android中,可以通过使用GridLayout布局管理器来实现六宫格布局。首先,在XML布局文件中定义一个GridLayout容器,并设置相关属性,如行数、列数、间距等。然后,在GridLayout中添加6个子视图,即代表六个方格的控件。 可以将六宫格布局分为两步骤:定义和设置属性与添加子视图。 在定义和设置属性方面,可以通过设置GridLayout的属性来实现六宫格布局的效果。比如,设置行数和列数为2,即可将布局分为2行3列的六个方格。可以使用layout_rowSpan和layout_columnSpan属性来设置某个子视图占据多个行或列的大小。也可以使用layout_gravity属性调整子视图在方格中的位置。 在添加子视图方面,可以使用GridLayout的addView方法来将子视图添加到布局中。可以使用LayoutInflater来实例化子视图,并为子视图设置相关属性。可以通过设置子视图的宽度和高度为0dp,以实现平均分配布局。 总结起来,通过使用GridLayout布局管理器,可实现六宫格布局,将界面划分为6个等宽、等高的方格。根据需要,可以通过设置各个子视图的属性和位置,来实现不同的布局效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尚思app

您的鼓励是我最大的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值