承接前两篇 ArkUI 组件内容,本文聚焦Grid 网格布局、List 列表高阶用法、组件懒加载、瀑布流等高频实用能力,结合完整案例讲解特性、适配方案与性能优化,适合页面布局进阶开发。

一、概述

在应用开发中,图文宫格、商品陈列、相册、分类菜单等场景,传统 Row/Column 布局已无法满足需求。Grid网格组件专门实现多列排布,而List除基础列表外,还支持分组、下拉刷新、上拉加载;配合懒加载可以大幅优化长列表、多图页面的内存占用与渲染速度,是大型应用必备技术。

二、Grid 网格布局组件

Grid 用于实现行列规则排列的宫格布局,广泛应用于功能菜单、相册、商品列表、图标导航。搭配GridItem作为列表项使用。

2.1 基础网格(固定列数)

核心参数:columnsTemplate 定义列数与列宽,rowsTemplate 定义行数,columnGap/rowGap 设置行列间距。

@Entry
@Component
struct GridBasicDemo {
  // 模拟宫格数据
  gridData: string[] = ["首页", "分类", "购物车", "我的", "消息", "设置"]

  build() {
    Column({ space: 20 }) {
      Text("基础4列网格布局").fontSize(20).fontWeight(FontWeight.Bold)
      
      Grid() {
        ForEach(this.gridData, (item: string) => {
          GridItem() {
            Column() {
              // 模拟图标区域
              Rect().width(50).height(50).fill("#007DFF").borderRadius(8)
              Text(item).fontSize(16).margin({ top: 8 })
            }
            .width("100%")
            .alignItems(HorizontalAlign.Center)
          }
        })
      }
      .columnsTemplate("1fr 1fr 1fr 1fr") // 均分4列
      .columnGap(15)
      .rowGap(20)
      .width("100%")
    }
    .padding(20)
  }
}

2.2 不等宽网格布局

通过不同比例 fr 单位,实现非均等分网格,适配复杂导航布局。

@Entry
@Component
struct GridUnequalDemo {
  build() {
    Column({ space: 20 }) {
      Text("不等宽网格布局").fontSize(20).fontWeight(FontWeight.Bold)
      Grid() {
        GridItem() { Text("模块1").width("100%").height(80).textAlign(TextAlign.Center).backgroundColor("#87CEEB") }
        GridItem() { Text("模块2").width("100%").height(80).textAlign(TextAlign.Center).backgroundColor("#98FB98") }
        GridItem() { Text("模块3").width("100%").height(80).textAlign(TextAlign.Center).backgroundColor("#FFD700") }
      }
      .columnsTemplate("2fr 1fr 1fr") // 第一列宽度是后两列总和
      .columnGap(10)
      .width("100%")
    }
    .padding(20)
  }
}

2.3 网格跨行列合并

通过 rowSpancolSpan 实现单元格跨行、跨列,制作不规则宫格。

@Entry
@Component
struct GridSpanDemo {
  build() {
    Grid() {
      // 跨2行
      GridItem({ rowSpan: 2 }) {
        Text("跨行2行")
          .width("100%")
          .height("100%")
          .textAlign(TextAlign.Center)
          .backgroundColor("#FFA07A")
      }
      GridItem() { Text("普通项1").width("100%").height(80).textAlign(TextAlign.Center).backgroundColor("#DDA0DD") }
      GridItem() { Text("普通项2").width("100%").height(80).textAlign(TextAlign.Center).backgroundColor("#DDA0DD") }
      // 跨2列
      GridItem({ colSpan: 2 }) {
        Text("跨列2列")
          .width("100%")
          .height(80)
          .textAlign(TextAlign.Center)
          .backgroundColor("#9370DB")
      }
    }
    .columnsTemplate("1fr 1fr 1fr")
    .rowGap(8)
    .columnGap(8)
    .width("100%")
    .padding(20)
  }
}

三、List 列表高阶用法

在前文基础上,拓展分组列表、下拉刷新、上拉加载三大实用功能,是资讯、聊天、商品列表的核心能力。

3.1 分组列表 ListItemGroup

用于实现带标题的分组列表,如通讯录、分类设置、多级菜单。

@Entry
@Component
struct ListGroupDemo {
  build() {
    List({ space: 5 }) {
      // 第一组
      ListItemGroup({ header: Text("常用功能").fontSize(18).fontWeight(FontWeight.Bold).padding(10) }) {
        ListItem() { Text("账号管理").width("100%").height(45).padding({ left: 15 }) }
        ListItem() { Text("安全中心").width("100%").height(45).padding({ left: 15 }) }
      }
      // 第二组
      ListItemGroup({ header: Text("系统设置").fontSize(18).fontWeight(FontWeight.Bold).padding(10) }) {
        ListItem() { Text("消息通知").width("100%").height(45).padding({ left: 15 }) }
        ListItem() { Text("隐私权限").width("100%").height(45).padding({ left: 15 }) }
      }
    }
    .width("100%")
    .height("100%")
  }
}

3.2 下拉刷新 + 上拉加载更多

结合 onRefreshonReachEnd 实现列表动态刷新与分页加载,模拟网络请求场景

@Entry
@Component
struct ListLoadDemo {
  @State listData: number[] = [1, 2, 3, 4, 5]
  @State isRefreshing: boolean = false

  // 下拉刷新
  onRefresh() {
    this.isRefreshing = true
    setTimeout(() => {
      this.listData = [1, 2, 3, 4, 5]
      this.isRefreshing = false
    }, 1500)
  }

  // 上拉加载
  loadMore() {
    let len = this.listData.length
    for (let i = len + 1; i <= len + 5; i++) {
      this.listData.push(i)
    }
  }

  build() {
    List({ space: 8 }) {
      ForEach(this.listData, (item: number) => {
        ListItem() {
          Text(`列表条目 ${item}`)
            .width("100%")
            .height(50)
            .textAlign(TextAlign.Start)
            .padding({ left: 20 })
            .backgroundColor("#F8F8F8")
        }
      })
    }
    .width("100%")
    .height("100%")
    .refresh({ refreshing: this.isRefreshing })
    .onRefresh(() => this.onRefresh())
    .onReachEnd(() => this.loadMore())
  }
}

四、组件懒加载(LazyForEach)

普通 ForEach 会一次性渲染所有列表项,数据量大时易卡顿、占用内存高。LazyForEach 实现可视区域懒加载,仅渲染屏幕内条目,是长列表性能优化核心方案。

4.1 懒加载基础使用

需要自定义数据类实现 ICollectionData 接口,适配懒加载数据源规则。

// 实现懒加载数据源
class BasicDataSource implements ICollectionData {
  private dataArr: string[]

  constructor(arr: string[]) {
    this.dataArr = arr
  }

  // 获取总数量
  getTotalCount(): number {
    return this.dataArr.length
  }

  // 获取单条数据
  getData(index: number): string {
    return this.dataArr[index]
  }

  // 数据变更通知
  setDataChangeListener(listener: DataChangeListener): void {}
}

@Entry
@Component
struct LazyForEachDemo {
  // 模拟100条数据
  private dataSource: BasicDataSource = new BasicDataSource(
    Array.from({ length: 100 }, (_, idx) => `懒加载条目 ${idx + 1}`)
  )

  build() {
    List() {
      LazyForEach(this.dataSource, (item: string) => {
        ListItem() {
          Text(item)
            .width("100%")
            .height(50)
            .padding({ left: 20 })
            .backgroundColor("#F5F5F5")
            .margin({ bottom: 5 })
        }
      })
    }
    .width("100%")
    .height("100%")
  }
}

4.2 Grid 结合懒加载

大量图片 / 宫格页面,搭配 LazyForEach 大幅提升页面流畅度。

@Entry
@Component
struct GridLazyDemo {
  private dataSource: BasicDataSource = new BasicDataSource(
    Array.from({ length: 60 }, (_, idx) => `宫格 ${idx + 1}`)
  )

  build() {
    Grid() {
      LazyForEach(this.dataSource, (item: string) => {
        GridItem() {
          Column() {
            Rect().width(60).height(60).fill("#4682B4").borderRadius(6)
            Text(item).fontSize(14).margin({ top: 6 })
          }
          .width("100%")
          .alignItems(HorizontalAlign.Center)
          .padding(10)
        }
      })
    }
    .columnsTemplate("1fr 1fr 1fr")
    .columnGap(12)
    .rowGap(15)
    .width("100%")
    .padding(15)
  }
}

五、综合实战:相册页面(Grid + 懒加载)

整合网格布局与懒加载,实现典型相册界面,可直接用于项目开发。

class ImageDataSource implements ICollectionData {
  private imgList: string[]
  constructor(list: string[]) {
    this.imgList = list
  }
  getTotalCount(): number { return this.imgList.length }
  getData(index: number): string { return this.imgList[index] }
  setDataChangeListener(listener: DataChangeListener): void {}
}

@Entry
@Component
struct PhotoAlbum {
  // 模拟相册数据
  albumData: ImageDataSource = new ImageDataSource(
    Array.from({ length: 80 }, (_, i) => `图片 ${i + 1}`)
  )

  build() {
    Column() {
      Text("我的相册")
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .padding(15)

      Grid() {
        LazyForEach(this.albumData, (name: string) => {
          GridItem() {
            Stack() {
              Rect().width("100%").height(120).fill("#B0C4DE")
              Text(name).fontColor(Color.White).fontSize(14)
            }
            .width("100%")
            .borderRadius(6)
          }
        })
      }
      .columnsTemplate("1fr 1fr 1fr")
      .columnGap(8)
      .rowGap(8)
      .layoutWeight(1)
    }
    .width("100%")
    .height("100%")
    .backgroundColor("#FFFFFF")
  }
}

六、开发总结与性能优化

6.1 布局组件选用规则

  1. 单列滚动列表、聊天页、资讯页:优先使用 List
  2. 多列图标、相册、商品宫格:优先使用 Grid
  3. 分组展示内容:使用 ListItemGroup 快速实现分组标题。

6.2 懒加载使用规范

  1. 数据量 超过 30 条 建议使用 LazyForEach,替代普通 ForEach
  2. 图片类页面必须开启懒加载,避免一次性加载大量图片造成 OOM;
  3. 懒加载数据源必须实现 ICollectionData 接口,保证数据驱动正常。

6.3 通用优化技巧

  1. 列表 / 网格项样式尽量简化,减少嵌套组件层级;
  2. 图片统一设置固定宽高,避免动态宽高引发布局抖动;
  3. 下拉刷新、上拉加载增加加载状态提示,提升交互体验。

七、拓展方向

  1. 实现瀑布流布局,打造图文资讯流页面;
  2. 结合路由跳转,实现点击列表 / 宫格项跳转到详情页;
  3. 学习列表侧滑删除、多选操作等高级交互。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐