【高心星出品】

鸿蒙PC开发——重复布局

重复布局是指在空间充足时,重复使用相同或相似的结构、组件或排列方式,用以展示更多内容、保持视觉一致性并提高用户体验。常用的重复布局包括列表布局、瀑布流布局、轮播布局和网格布局。

响应式布局方式 典型布局场景
列表布局 List组件+断点
瀑布流布局 WaterFlow组件+断点
轮播布局 Swiper组件+断点
网格布局 Grid组件+断点

媒体查询工具类

export class WidthBreakpointType<T> {
  sm: T;
  md: T;
  lg: T;
  // 构造函数
  constructor(sm: T, md: T, lg: T) {
    this.sm = sm;
    this.md = md;
    this.lg = lg;
  }
  // 根据断点返回值
  getValue(widthBp: WidthBreakpoint): T {
    if (widthBp === WidthBreakpoint.WIDTH_XS || widthBp === WidthBreakpoint.WIDTH_SM) {
      return this.sm;
    }
    if (widthBp === WidthBreakpoint.WIDTH_MD) {
      return this.md;
    } else {
      return this.lg;
    }
  }
}

列表布局

列表布局基于横向断点,动态调整列数以实现重复布局。

布局效果

在这里插入图片描述

实现方案

设置不同横向断点下,List组件的lanes、space属性实现目标效果。

参考代码

List({
  space: new WidthBreakpointType(8, 12, 16, 16).getValue(this.mainWindowInfo.widthBp),
  scroller: this.listScroller
}) {
  // ...
}
.scrollBar(BarState.Off)
.lanes(new WidthBreakpointType(1, 2, 3, 3).getValue(this.mainWindowInfo.widthBp), 12)

代码逻辑走读:

  1. 创建列表组件:使用List构造函数初始化一个列表组件,设置space属性为根据当前窗口宽度动态计算的值,确保列表项之间有适当的间距。同时,指定scroller属性为this.listScroller,用于控制列表的滚动行为。
  2. 配置滚动条:通过调用.scrollBar(BarState.Off)方法,将列表的滚动条状态设置为关闭,这意味着用户在浏览列表时不会看到滚动条。
  3. 设置列表行数和宽度:使用.lanes()方法,根据当前窗口宽度动态设置列表的行数和宽度。getValue(this.mainWindowInfo.widthBp)方法用于获取当前窗口宽度对应的值,确保列表布局在不同设备上都能良好显示。

瀑布流布局

瀑布流布局基于横向断点,动态控制列数以实现重复布局。

布局效果
在这里插入图片描述

实现方案

设置不同横向断点下,WaterFlow组件的columnsTemplate属性实现目标效果。

示例代码

WaterFlow() {
  LazyForEach(this.dataSource, (item: number, index: number) => {
    FlowItem() {
      Row() {}
      .width('100%')
      .height('100%')
      .borderRadius(16)
      .backgroundColor('#F1F3F5')
    }
    .width('100%')
    .height(this.itemHeightArray[index])
  }, (item: number, index: number) => JSON.stringify(item) + index)
}
.columnsTemplate(`repeat(${new WidthBreakpointType(2, 3, 4, 4).getValue(this.mainWindowInfo.widthBp)}, 1fr)`)
.columnsGap(12)
.rowsGap(12)
.width('100%')

代码逻辑走读:

  1. 组件定义:定义了一个名为WaterFlow的组件。

  2. 数据遍历:使用LazyForEach遍历dataSource数据源,为每个数据项创建一个FlowItem组件。

  3. FlowItem组件创建

    • 每个FlowItem包含一个Row布局。
    • Row布局的样式属性设置为宽度100%,高度根据数据项的索引动态调整,圆角为16,背景色为#F1F3F5
  4. 响应式布局:通过columnsTemplate属性,根据屏幕宽度变化动态调整列数,使用columnsGaprowsGap属性设置列间距和行间距。

  5. 宽度设置:整个WaterFlow组件的宽度设置为100%。

轮播布局

轮播布局基于横向断点,动态控制视窗内显示元素的个数以实现重复布局。

布局效果

在这里插入图片描述

实现方案

设置不同横向断点下,Swiper组件的displayCount、prevMargin、nextMargin和indicator属性实现目标效果。

示例代码

Swiper() {
  // ...
}
.displayCount(new WidthBreakpointType(1, 2, 3, 3).getValue(this.mainWindowInfo.widthBp))
// Setting the navigation point Style of the swiper.
.indicator(this.mainWindowInfo.widthBp === WidthBreakpoint.WIDTH_SM ? Indicator.dot()
  .itemWidth(6)
  .itemHeight(6)
  .selectedItemWidth(12)
  .selectedItemHeight(6)
  .color('#4DFFFFFF')
  .selectedColor(Color.White) : false
)
// The sizes of the front and rear banners on the MD and LG devices are different.
.prevMargin(new WidthBreakpointType(0, 12, 64, 64).getValue(this.mainWindowInfo.widthBp))
.nextMargin(new WidthBreakpointType(0, 12, 64, 64).getValue(this.mainWindowInfo.widthBp))

代码逻辑走读:

  1. Swiper组件定义

    • 使用Swiper()定义了一个轮播图组件。
  2. 设置显示数量

    • .displayCount(new WidthBreakpointType(1, 2, 3, 3).getValue(this.mainWindowInfo.widthBp)):根据当前窗口的宽度断点(widthBp)设置显示的轮播图数量。
  3. 设置导航点样式

    • .indicator(this.mainWindowInfo.widthBp === WidthBreakpoint.WIDTH_SM ? Indicator.dot()...
      
      • 判断当前窗口宽度是否为WIDTH_SM(小屏幕),如果是,则设置导航点样式为圆点(Indicator.dot())。
      • 设置导航点的宽度、高度、颜色等样式属性。
    • 如果不是小屏幕,则不显示导航点(false)。

  4. 设置前后轮播图的边距

    • .prevMargin(new WidthBreakpointType(0, 12, 64, 64).getValue(this.mainWindowInfo.widthBp))
      
      • 根据当前窗口的宽度断点设置前一个轮播图的边距。
    • .nextMargin(new WidthBreakpointType(0, 12, 64, 64).getValue(this.mainWindowInfo.widthBp))
      
      • 根据当前窗口的宽度断点设置后一个轮播图的边距。

网格布局

网格布局基于横向断点,动态控制列数/行数以实现重复布局。

布局效果

网格布局将容器按行和列划分为规则的网格,每个子组件严格对齐,实现均分和占比能力,详情请参考创建网格;瀑布流布局子组件高度支持自定义,无需对齐,适合展示高度不一的内容,详情请参考创建瀑布流。

实现方案

设置不同横向断点下,Grid组件的columnsTemplate属性实现目标效果。在不设置Grid组件行数的情况下,行数 = 展示元素数量 / 列数。Grid组件其他布局模式,请参考rowsTemplate。

示例代码

Grid() {
  ForEach(this.infoArray.slice(new WidthBreakpointType(4, 2, 0, 0).getValue(this.mainWindowInfo.widthBp)),
    (item: number) => {
      // ...
    }, (item: number, index: number) => JSON.stringify(item) + index)
}
.width('100%')
.columnsTemplate(`repeat(${new WidthBreakpointType(2, 3, 4, 4).getValue(this.mainWindowInfo.widthBp)}, 1fr)`)
.columnsGap(12)
.rowsGap(12)

代码逻辑走读:

  1. 网格定义:使用 Grid()定义了一个网格布局容器。
  2. 动态迭代:通过 ForEach方法遍历 infoArray数组,数组长度由 WidthBreakpointType对象的 getValue方法根据 mainWindowInfo.widthBp计算得出。
  3. 元素处理:对于数组中的每个元素(item),执行一个匿名函数进行处理(具体处理逻辑未提供)。
  4. 唯一键生成:使用 JSON.stringify(item) + index作为每个元素的唯一键。
  5. 网格样式设置
    • 设置网格宽度为 100%
    • 使用 columnsTemplate方法定义列模板,列数由 WidthBreakpointType对象的 getValue方法计算得出。
    • 设置列间距为 12,行间距为 12
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐