157.[HarmonyOS NEXT 实战案例一:Grid] 基础网格布局:打造精美电商商品列表

2025-06-30 22:39:23
103次阅读
0个评论

[HarmonyOS NEXT 实战案例一:Grid] 基础网格布局:打造精美电商商品列表

项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star

效果演示

image.png

1. Grid组件概述

在HarmonyOS NEXT的ArkUI框架中,Grid组件是一种强大的网格容器,它与GridItem子组件一起使用,可以创建灵活的网格布局。网格布局是由"行"和"列"分割的单元格组成,通过指定"项目"所在的单元格,可以实现各种各样的布局效果。

1.1 Grid与GridItem的关系

  • Grid:网格容器组件,用于设置网格布局相关参数
  • GridItem:网格子项组件,定义子组件相关特征
  • Grid的子组件必须是GridItem组件

1.2 Grid组件的主要特性

特性 描述
自定义行列数 可以通过rowsTemplate和columnsTemplate属性设置网格的行数和列数
尺寸占比控制 可以控制每行每列的尺寸占比
子组件跨行列 可以设置子组件横跨几行或几列
布局方向 支持垂直和水平布局
间距控制 可以设置行间距和列间距
滚动能力 支持构建可滚动的网格布局

2. 电商商品列表实战

在本案例中,我们将使用Grid和GridItem组件创建一个电商应用的商品列表页面,展示Apple Store的产品。

2.1 页面结构概览

我们的电商商品列表页面包含以下几个部分:

  1. 顶部标题栏:显示应用名称和操作按钮
  2. 分类标签:显示商品分类和查看更多选项
  3. 商品网格:使用Grid和GridItem展示商品信息
  4. 底部导航栏:提供应用的主要导航选项

2.2 数据模型定义

首先,我们定义商品数据模型:

interface Product {
    id: number,
    name: string,
    price: number,
    image: Resource,
    discount?: number
}

然后,创建商品数据数组:

@State products:Product[] = [
    { id: 1, name: 'iPhone 15 Pro', price: 7999, image: $r('app.media.phone'), discount: 10 },
    { id: 2, name: 'MacBook Pro', price: 12999, image: $r('app.media.big15') },
    { id: 3, name: 'iPad Air', price: 4399, image: $r('app.media.big14'), discount: 5 },
    // 更多商品...
]

2.3 顶部标题栏实现

// 顶部标题栏
Row() {
    Text('Apple Store')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1D1D1F')

    Blank()

    Button() {
        Image($r('app.media.search_icon'))
            .width(24)
            .height(24)
    }
    .width(40)
    .height(40)
    .borderRadius(20)
    .backgroundColor('#F5F5F7')

    Button() {
        Image($r('app.media.cart_icon'))
            .width(24)
            .height(24)
    }
    .width(40)
    .height(40)
    .borderRadius(20)
    .backgroundColor('#F5F5F7')
    .margin({ left: 12 })
}
.width('100%')
.padding({ left: 20, right: 20, top: 10, bottom: 10 })
.backgroundColor('#FFFFFF')

2.4 分类标签实现

// 分类标签
Row() {
    Text('热门商品')
        .fontSize(18)
        .fontWeight(FontWeight.Medium)
        .fontColor('#1D1D1F')

    Blank()

    Text('查看全部')
        .fontSize(14)
        .fontColor('#007AFF')
}
.width('100%')
.padding({ left: 20, right: 20, top: 16, bottom: 16 })
.backgroundColor('#FFFFFF')

3. Grid网格布局实现

3.1 Grid容器配置

Grid() {
    // GridItem子项...
}
.columnsTemplate('1fr 1fr') // 两列布局
.columnsGap(16)             // 列间距
.rowsGap(16)                // 行间距
.width('100%')
.layoutWeight(1)
.padding({ left: 20, right: 20, bottom: 20 })
.backgroundColor('#F2F2F7')

在这个配置中:

  • columnsTemplate('1fr 1fr'):设置两列布局,每列占比相等
  • columnsGap(16):设置列间距为16vp
  • rowsGap(16):设置行间距为16vp

3.2 GridItem商品卡片实现

我们使用ForEach循环遍历商品数据,为每个商品创建一个GridItem:

ForEach(this.products, (product:Product) => {
    GridItem() {
        Column() {
            // 商品图片容器
            Stack({ alignContent: Alignment.TopEnd }) {
                Image(product.image)
                    .width('100%')
                    .height(120)
                    .objectFit(ImageFit.Contain)
                    .backgroundColor('#F8F8F8')
                    .borderRadius(12)

                // 折扣标签
                if (product.discount) {
                    Text(`-${product.discount}%`)
                        .fontSize(12)
                        .fontColor('#FFFFFF')
                        .backgroundColor('#FF3B30')
                        .padding({ left: 8, right: 8, top: 4, bottom: 4 })
                        .borderRadius(8)
                        .margin({ top: 8, right: 8 })
                }
            }
            .width('100%')
            .height(120)

            // 商品信息
            Column() {
                Text(product.name)
                    .fontSize(14)
                    .fontWeight(FontWeight.Medium)
                    .fontColor('#1D1D1F')
                    .maxLines(2)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
                    .margin({ top: 12 })

                Row() {
                    if (product.discount) {
                        Text(`¥${(product.price * (100 - product.discount) / 100).toFixed(0)}`)
                            .fontSize(16)
                            .fontWeight(FontWeight.Bold)
                            .fontColor('#FF3B30')

                        Text(`¥${product.price}`)
                            .fontSize(12)
                            .fontColor('#8E8E93')
                            .decoration({ type: TextDecorationType.LineThrough })
                            .margin({ left: 4 })
                    } else {
                        Text(`¥${product.price}`)
                            .fontSize(16)
                            .fontWeight(FontWeight.Bold)
                            .fontColor('#1D1D1F')
                    }

                    Blank()

                    Button() {
                        Image($r('app.media.add_icon'))
                            .width(16)
                            .height(16)
                            .fillColor('#FFFFFF')
                    }
                    .width(28)
                    .height(28)
                    .borderRadius(14)
                    .backgroundColor('#007AFF')
                }
                .width('100%')
                .margin({ top: 8 })
            }
            .alignItems(HorizontalAlign.Start)
            .width('100%')
        }
        .width('100%')
        .padding(12)
        .backgroundColor('#FFFFFF')
        .borderRadius(16)
        .shadow({
            radius: 8,
            color: 'rgba(0, 0, 0, 0.1)',
            offsetX: 0,
            offsetY: 2
        })
    }
    .onClick(() => {
        console.log(`点击了商品: ${product.name}`)
    })
})

每个GridItem包含:

  1. 商品图片:使用Stack布局,支持在右上角显示折扣标签
  2. 商品名称:支持最多显示两行,超出部分使用省略号
  3. 价格信息:显示原价和折扣价(如果有折扣)
  4. 添加按钮:用于将商品添加到购物车

3.3 底部导航栏实现

// 底部导航栏
Row() {
    Column() {
        Image($r('app.media.home_icon'))
            .width(24)
            .height(24)
            .fillColor('#007AFF')

        Text('首页')
            .fontSize(12)
            .fontColor('#007AFF')
            .margin({ top: 4 })
    }
    .layoutWeight(1)

    // 其他导航项...
}
.width('100%')
.height(60)
.backgroundColor('#FFFFFF')
.borderColor('#E5E5EA')
.borderWidth({ top: 1 })

4. Grid组件的关键属性详解

4.1 行列设置

属性 描述 示例
columnsTemplate 设置网格布局的列数和每列的尺寸占比 '1fr 2fr':两列布局,第二列是第一列的两倍宽
rowsTemplate 设置网格布局的行数和每行的尺寸占比 '1fr 1fr 1fr':三行布局,每行高度相等
columnsGap 设置列间距 columnsGap(16)
rowsGap 设置行间距 rowsGap(16)

4.2 GridItem定位属性

属性 描述 示例
rowStart 设置起始行号 rowStart(1)
rowEnd 设置结束行号 rowEnd(3)
columnStart 设置起始列号 columnStart(2)
columnEnd 设置结束列号 columnEnd(4)

4.3 布局方向

Grid() {
  // 子项...
}
.layoutDirection(GridDirection.Row) // 水平方向布局

GridDirection枚举值:

  • GridDirection.Row:水平方向布局(默认)
  • GridDirection.Column:垂直方向布局

5. 布局技巧与最佳实践

5.1 响应式网格布局

在不同屏幕尺寸下,可以动态调整列数:

// 根据屏幕宽度调整列数
let columns = '1fr 1fr';
if (screenWidth >= 600) {
  columns = '1fr 1fr 1fr';
}
if (screenWidth >= 840) {
  columns = '1fr 1fr 1fr 1fr';
}

Grid() {
  // 子项...
}
.columnsTemplate(columns)

5.2 商品卡片设计技巧

  1. 阴影效果:使用shadow属性为卡片添加阴影,增强立体感
  2. 圆角处理:为卡片和图片添加borderRadius,使界面更加圆润
  3. 折扣标签:使用条件渲染显示折扣标签,突出促销商品
  4. 文本溢出处理:使用maxLines和textOverflow处理长文本

5.3 网格间距优化

适当的网格间距可以提升用户体验:

  • 列间距(columnsGap):通常设置为12-20vp
  • 行间距(rowsGap):通常设置为16-24vp

6. 总结

在本教程中,我们学习了如何使用HarmonyOS NEXT的Grid和GridItem组件创建一个电商商品列表页面。我们详细讲解了Grid组件的基本概念、主要属性和使用方法,以及如何通过合理的布局和样式设计,打造美观实用的商品卡片。

收藏00

登录 后评论。没有帐号? 注册 一个。

全栈若城

  • 0回答
  • 4粉丝
  • 0关注