🌟 引言:二维布局的强大力量

在移动应用界面设计中,网格布局是组织内容的经典模式。无论是照片墙、商品网格还是仪表盘,网格都能提供清晰的视觉结构和高效的空间利用。ArkUI的Grid组件专为这类场景设计,它不同于线性布局的单维度排列,而是真正的二维布局系统,可以同时在行和列方向上精确控制子组件的位置和大小。

一、Grid组件基础:构建规则网格系统

Grid是ArkUI中用于创建网格布局的核心容器组件,它通过定义行和列的模板来构建规则的网格结构。

1. 基本结构与关键属性

@Entry
@Component
struct BasicGridExample {
  build() {
    Column() {
      Grid() {
        ForEach(Array.from({ length: 9 }), (_, index) => {
          GridItem() {
            Text(`Item ${index + 1}`)
              .fontSize(16)
              .textAlign(TextAlign.Center)
          }
          .backgroundColor(0xF5F5F5)
          .borderRadius(8)
        })
      }
      .columnsTemplate('1fr 1fr 1fr') // 3等分列
      .rowsTemplate('1fr 1fr 1fr')    // 3等分行
      .columnsGap(12)                 // 列间距
      .rowsGap(12)                   // 行间距
      .height(300)
    }
    .padding(20)
  }
}

关键属性解析:

  • columnsTemplate/rowsTemplate:定义网格的列和行结构,支持多种单位
  • columnsGap/rowsGap:设置网格项之间的水平和垂直间距
  • scrollBar:控制滚动条显示策略,支持BarState.Off/BarState.Auto/BarState.On

2. 灵活的模板定义方式

Grid支持多种模板定义方式,适应不同布局需求:

// 等分列:3列等宽
.columnsTemplate('1fr 1fr 1fr')

// 混合单位:固定宽度与弹性宽度结合  
.columnsTemplate('100px 1fr 2fr')

// 重复模式:使用repeat函数简化定义
.columnsTemplate('repeat(4, 1fr)')

// 响应式断点:根据屏幕尺寸动态调整
.columnsTemplate(this.getColumnsTemplate())

// 根据屏幕宽度动态返回模板
getColumnsTemplate(): string {
  const screenWidth = display.getDefaultDisplaySync().width
  if (screenWidth < 600) {
    return '1fr' // 小屏幕单列
  } else if (screenWidth < 840) {
    return '1fr 1fr' // 中屏幕双列
  } else {
    return 'repeat(3, 1fr)' // 大屏幕三列
  }
}
二、GridItem的高级用法:不规则网格与跨列布局

在实际应用中,经常需要创建不规则的网格布局,如某些项跨多列或多行。ArkUI提供了多种方式实现这种效果。

1. 传统的跨列布局方式

@Component
struct IrregularGridExample {
  @State items: number[] = Array.from({ length: 10 }, (_, i) => i + 1)
  
  build() {
    Grid() {
      ForEach(this.items, (item: number, index: number) => {
        GridItem() {
          Text(`项目 ${item}`)
            .fontSize(14)
            .textAlign(TextAlign.Center)
        }
        .backgroundColor(this.getBackgroundColor(index))
        // 实现不规则布局:每行的第一个项目跨两列
        .columnStart(this.getColumnStart(index))
        .columnEnd(this.getColumnEnd(index))
      })
    }
    .columnsTemplate('1fr 1fr 1fr') // 3列基础网格
    .columnsGap(10)
    .rowsGap(10)
  }
  
  // 计算列起始位置
  getColumnStart(index: number): number {
    return (index % 3 === 0) ? 0 : (index % 3)
  }
  
  // 计算列结束位置
  getColumnEnd(index: number): number {
    return (index % 3 === 0) ? 2 : (index % 3)
  }
  
  // 根据位置设置不同背景色
  getBackgroundColor(index: number): number {
    const colors = [0xEF9A9A, 0x90CAF9, 0xA5D6A7, 0xFFFF72]
    return colors[index % colors.length]
  }
}

2. 性能优化:GridLayoutOptions方案

当处理大量数据时,传统的columnStart/columnEnd方式可能导致性能问题。ArkUI API 12引入了GridLayoutOptions来优化这种情况。

class GridLayoutConfig {
  regularSize: [number, number] = [1, 1] // 默认每个项占1行1列
  irregularIndexes: number[] = []         // 不规则项的索引数组
}

@Entry
@Component
struct OptimizedGridExample {
  private dataSource: MyDataSource = new MyDataSource()
  private irregularIndexes: number[] = []
  private layoutOptions: GridLayoutOptions = {
    regularSize: [1, 1],
    irregularIndexes: this.irregularIndexes
  }
  
  aboutToAppear() {
    // 预计算不规则项:每4个中的第1个跨2列
    for (let i = 0; i < 1000; i++) {
      if (i % 4 === 0) {
        this.irregularIndexes.push(i)
      }
    }
  }
  
  build() {
    Grid(new Scroller(), this.layoutOptions) {
      LazyForEach(this.dataSource, (item: string, index: number) => {
        GridItem() {
          OptimizedGridItem({ content: item })
        }
      }, (item: string) => item)
    }
    .columnsTemplate('1fr 1fr 1fr')
    .cachedCount(1) // 预加载优化
  }
}

@Component
struct OptimizedGridItem {
  @Prop content: string
  
  build() {
    Column() {
      Text(this.content)
        .fontSize(16)
      Text('高性能网格项')
        .fontSize(12)
        .fontColor(Color.Gray)
    }
    .padding(10)
    .backgroundColor(0xF5F5F5)
    .borderRadius(8)
    .width('100%')
    .height(80)
  }
}
三、性能优化深度解析

1. 懒加载与缓存策略

对于大数据集的网格布局,必须采用懒加载机制避免内存溢出。

class ProductDataSource implements IDataSource {
  private productList: Product[] = []
  
  totalCount(): number {
    return this.productList.length
  }
  
  getData(index: number): Product {
    return this.productList[index]
  }
  
  registerDataChangeListener(listener: DataChangeListener): void {}
  unregisterDataChangeListener(listener: DataChangeListener): void {}
}

@Entry
@Component
struct ProductGrid {
  private data: ProductDataSource = new ProductDataSource()
  private scroller: Scroller = new Scroller()
  
  build() {
    Grid(this.scroller) {
      LazyForEach(this.data, (product: Product) => {
        GridItem() {
          ProductCard({ product: product })
        }
      }, (product: Product) => product.id.toString())
    }
    .columnsTemplate('1fr 1fr')
    .cachedCount(2) // 前后各缓存2屏内容
    .onScrollIndex((first: number) => {
      // 滚动索引变化监听,可用于分页加载
      console.info(`当前首项索引: ${first}`)
    })
  }
}

2. 组件复用机制

通过@Reusable装饰器实现组件复用,进一步提升滚动性能。

@Reusable
@Component
struct ProductCard {
  @State product: Product | undefined
  
  aboutToReuse(params: Record<string, Object>): void {
    this.product = params.product as Product
  }
  
  build() {
    Column() {
      Image(this.product?.image)
        .objectFit(ImageFit.Contain)
        .height(60)
      Text(this.product?.name)
        .fontSize(14)
        .margin({ top: 5 })
      Text(`¥${this.product?.price}`)
        .fontSize(12)
        .fontColor(Color.Red)
    }
    .padding(8)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow(ShadowStyle.OUTER_DEFAULT)
  }
}
四、复杂网格布局实战

1. 瀑布流布局实现

虽然Grid主要针对规则网格,但通过巧妙配置可以实现类瀑布流效果。

@Component
struct WaterfallGrid {
  @State items: WaterfallItem[] = []
  
  build() {
    Grid() {
      ForEach(this.items, (item: WaterfallItem) => {
        GridItem() {
          WaterfallCard({ item: item })
        }
        .rowStart(item.rowStart)
        .rowEnd(item.rowEnd)
        .columnStart(item.columnStart) 
        .columnEnd(item.columnEnd)
      })
    }
    .columnsTemplate('1fr 1fr') // 双列瀑布流
    .rowsTemplate('repeat(auto-fill, 100px)') // 自动行高
  }
}

2. 交互增强:拖拽排序与动画

@Component
struct DraggableGrid {
  @State items: GridItemData[] = []
  @State draggedIndex: number = -1
  
  build() {
    Grid() {
      ForEach(this.items, (item: GridItemData, index: number) => {
        GridItem() {
          DraggableItem({ 
            item: item,
            index: index,
            onDragStart: this.onDragStart.bind(this),
            onDragEnd: this.onDragEnd.bind(this)
          })
        }
        .height(100)
    })
    .columnsTemplate('repeat(3, 1fr)')
  }
  
  // 拖拽开始处理
  onDragStart(index: number): void {
    this.draggedIndex = index
    // 添加拖拽视觉反馈
  }
  
  // 拖拽结束处理
  onDragEnd(targetIndex: number): void {
    if (this.draggedIndex >= 0) {
      // 执行数据重排序
      this.reorderItems(this.draggedIndex, targetIndex)
      this.draggedIndex = -1
    }
  }
}
五、多设备适配策略

1. 响应式断点系统

@Entry
@Component
struct ResponsiveGrid {
  @State currentLayout: LayoutType = LayoutType.COMPACT
  
  aboutToAppear() {
    this.updateLayout(display.getDefaultDisplaySync().width)
  }
  
  build() {
    Grid() {
      // 网格内容
    }
    .columnsTemplate(this.getColumnsTemplate())
    .rowsTemplate(this.getRowsTemplate())
    .onAreaChange((oldValue, newValue) => {
      // 监听窗口变化,动态调整布局
      this.updateLayout(newValue.width)
    })
  }
  
  getColumnsTemplate(): string {
    switch (this.currentLayout) {
      case LayoutType.COMPACT: return '1fr'
      case LayoutType.MEDIUM: return '1fr 1fr'  
      case LayoutType.EXPANDED: return 'repeat(3, 1fr)'
      default: return '1fr'
    }
  }
  
  updateLayout(screenWidth: number): void {
    if (screenWidth < 600) {
      this.currentLayout = LayoutType.COMPACT
    } else if (screenWidth < 840) {
      this.currentLayout = LayoutType.MEDIUM
    } else {
      this.currentLayout = LayoutType.EXPANDED
    }
  }
}

2. 横竖屏适配

@Component
struct AdaptiveGrid {
  @StorageLink('orientation') orientation: number = 0
  
  build() {
    Grid() {
      // 网格内容
    }
    .columnsTemplate(this.getLandscapeTemplate())
  }
  
  getLandscapeTemplate(): string {
    // 横屏时增加列数,竖屏时减少列数
    return this.orientation === 1 ? 'repeat(4, 1fr)' : 'repeat(2, 1fr)'
  }
}
六、调试与性能监控

1. 布局检查器使用

通过DevEco Studio的布局检查器分析网格布局性能:

  • 检查网格线对齐情况
  • 监控组件创建和销毁频率
  • 分析重排和重绘性能

2. 性能打点分析

import hiTraceMeter from '@ohos.hiTraceMeter'

@Component
struct MeasuredGrid {
  build() {
    Grid() {
      // 网格内容
    }
    .onAppear(() => {
      hiTraceMeter.startTrace('grid_rendering', 1)
    })
    .onDisAppear(() => {
      hiTraceMeter.finishTrace('grid_rendering', 1)
    })
  }
}
💎 总结

Grid网格布局是鸿蒙应用开发中处理二维布局场景的利器。通过掌握基础网格构建、不规则布局实现、性能优化策略和多设备适配技巧,开发者可以创建出既美观又高效的网格界面。关键在于根据具体场景选择合适的实现方案,平衡功能需求与性能表现。

本系列下一篇文章将探讨《自定义组件实战:构建可复用的UI组件与组件间通信》,深入分析如何创建高复用性的自定义组件体系。

进一步学习建议:在实际项目中,建议从简单网格开始,逐步添加复杂功能,并使用性能分析工具持续监控优化效果。官方文档中的Grid组件详解提供了完整的API参考。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐