[HarmonyOS NEXT 实战案例:电商应用] 基础篇 - 垂直分割布局打造商品详情页

2025-06-11 23:27:37
111次阅读
0个评论

[HarmonyOS NEXT 实战案例:电商应用] 基础篇 - 垂直分割布局打造商品详情页

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

效果演示

image.png

引言

电商应用是移动应用开发中的重要场景之一,其中商品详情页是用户了解商品信息、做出购买决策的关键界面。一个设计良好的商品详情页需要清晰展示商品图片、价格、规格等信息,并提供便捷的购买操作。本教程将详细讲解如何使用HarmonyOS NEXT的ColumnSplit组件构建一个电商商品详情页,通过垂直分割布局将界面分为商品图片区域和商品信息区域。

组件概述

在本案例中,我们将使用以下HarmonyOS NEXT组件:

组件名称 功能描述
ColumnSplit 垂直分割布局容器,将界面分为上下两部分
Column 垂直布局容器,用于垂直排列子组件
Row 水平布局容器,用于水平排列子组件
Swiper 轮播图组件,用于展示商品的多张图片
Image 图片组件,用于显示商品图片
Text 文本组件,用于显示商品名称、价格等信息
Button 按钮组件,用于收藏、加入购物车和购买操作
ForEach 循环渲染组件,用于渲染商品规格选项和轮播图
Circle 圆形组件,用于创建轮播图指示器
Scroll 滚动容器,用于在有限空间内展示更多内容
Flex 弹性布局容器,用于灵活排列子组件

代码实现

组件结构

首先,我们定义了一个名为ECommerceDetailExample的自定义组件:

@Component
export struct ECommerceDetailExample {
    @State currentImageIndex: number = 0
    @State selectedSize: string = 'M'
    @State quantity: number = 1
    @State isFavorite: boolean = false

    build() {
        // 组件内容
    }
}

在这个组件中,我们定义了四个状态变量:

  • currentImageIndex:当前显示的商品图片索引
  • selectedSize:当前选中的商品尺码
  • quantity:商品数量
  • isFavorite:是否收藏商品

外层容器

我们使用ColumnSplit组件作为最外层容器,将界面分为上下两部分:

ColumnSplit() {
    // 商品图片展示区
    Column() {
        // 图片轮播和指示器
    }
    .height('40%')

    // 商品信息区
    Column() {
        // 商品信息和操作按钮
    }
}
.height(600)

ColumnSplit设置了600的高度,确保整个界面有足够的显示空间。上部分的商品图片展示区占总高度的40%,下部分的商品信息区占剩余高度。

商品图片展示区

商品图片展示区包含一个轮播图组件和一个图片指示器:

Column() {
    Swiper() {
        ForEach([
            $r('app.media.big19'),
            $r('app.media.big18'),
            $r('app.media.big17')
        ], (img: Resource, index: number) => {
            Image(img)
                .width('100%')
                .height(300)
                .objectFit(ImageFit.Cover)
        })
    }
    .indicatorStyle({
        color: '#ff4757',
        selectedColor: '#ff4757'
    })
    .onChange((index: number) => {
        this.currentImageIndex = index
    })

    // 图片指示器
    Row() {
        ForEach([0, 1, 2], (index: number) => {
            Circle()
                .width(8)
                .height(8)
                .margin(3)
                .fill(index === this.currentImageIndex ? '#ff4757' : '#cccccc')
        })
    }
    .margin({ top: 10 })
    .justifyContent(FlexAlign.Center)
}
.height('40%')

在这个部分:

  1. 我们使用Swiper组件创建一个轮播图,通过ForEach循环渲染三张商品图片。
  2. 每张图片都设置为100%宽度和300的高度,并使用objectFit(ImageFit.Cover)确保图片填充整个区域。
  3. 我们设置了轮播图的指示器样式,并通过onChange事件更新currentImageIndex状态。
  4. 在轮播图下方,我们创建了一个自定义的图片指示器,使用Circle组件表示每张图片,当前显示的图片对应的圆点会高亮显示。

商品信息区

商品信息区包含商品标题、价格、规格选择、数量选择、商品详情和底部操作栏:

Column() {
    Scroll() {
        Column() {
            // 商品标题和价格
            Row() {
                Column() {
                    Text('男士休闲衬衫')
                        .fontSize(20)
                        .fontWeight(FontWeight.Bold)
                    Text('夏季新款 透气舒适')
                        .fontSize(14)
                        .fontColor('#666666')
                }
                .layoutWeight(1)

                Button(this.isFavorite ? $r('app.media.01') : $r('app.media.02'))
                    .width(24)
                    .height(24)
                    .backgroundColor(Color.Transparent)
                    .onClick(() => this.isFavorite = !this.isFavorite)
            }
            .margin({ bottom: 15 })

            Text('¥189')
                .fontSize(24)
                .fontColor('#ff4757')
                .margin({ bottom: 20 })

            // 商品规格选择
            Column() {
                Text('尺码')
                    .fontSize(16)
                    .fontWeight(FontWeight.Bold)
                    .margin({ bottom: 10 })

                Flex({ wrap: FlexWrap.Wrap }) {
                    ForEach(['XS', 'S', 'M', 'L', 'XL'], (size: string) => {
                        Button(size)
                            .width(60)
                            .height(40)
                            .fontSize(14)
                            .backgroundColor(this.selectedSize === size ? '#ff4757' : '#f5f5f5')
                            .fontColor(this.selectedSize === size ? '#ffffff' : '#333333')
                            .margin({ right: 10, bottom: 10 })
                            .onClick(() => this.selectedSize = size)
                    })
                }
            }
            .margin({ bottom: 20 })

            // 商品数量选择
            Column() {
                Text('数量')
                    .fontSize(16)
                    .fontWeight(FontWeight.Bold)
                    .margin({ bottom: 10 })

                Row() {
                    Button('-')
                        .width(40)
                        .height(40)
                        .fontSize(18)
                        .onClick(() => {
                            if (this.quantity > 1) this.quantity--
                        })

                    Text(this.quantity.toString())
                        .fontSize(16)
                        .margin({ left: 15, right: 15 })
                        .width(40)
                        .textAlign(TextAlign.Center)

                    Button('+')
                        .width(40)
                        .height(40)
                        .fontSize(18)
                        .onClick(() => this.quantity++)
                }
            }
            .margin({ bottom: 20 })

            // 商品详情
            Column() {
                Text('商品详情')
                    .fontSize(18)
                    .fontWeight(FontWeight.Bold)
                    .margin({ bottom: 10 })

                Text('材质: 100%棉\n颜色: 浅蓝色\n款式: 休闲\n季节: 夏季\n洗涤说明: 建议手洗,不可漂白')
                    .fontSize(14)
                    .lineHeight(22)
            }
            .margin({ bottom: 20 })
        }
        .padding(20)
    }
    .layoutWeight(1)

    // 底部操作栏
    Row() {
        Button('加入购物车')
            .height(50)
            .layoutWeight(1)
            .backgroundColor('#ff9500')
            .fontColor('#ffffff')

        Button('立即购买')
            .height(50)
            .layoutWeight(1)
            .backgroundColor('#ff4757')
            .fontColor('#ffffff')
            .margin({ left: 10 })
    }
    .padding(10)
    .backgroundColor('#ffffff')
    .border({
        width:{top:1,bottom:0, left:0,right:0},
        color:'#eee',
        style:{
            top:BorderStyle.Solid,
        }
    })
}

在这个部分:

  1. 我们使用Scroll组件创建一个可滚动的区域,确保在内容超出可视区域时用户可以滚动查看。
  2. 商品标题和价格部分使用RowColumn组合布局,右侧添加了一个收藏按钮,点击可以切换收藏状态。
  3. 商品规格选择部分使用Flex组件创建一个弹性布局,通过ForEach循环渲染尺码选项,当前选中的尺码会高亮显示。
  4. 商品数量选择部分使用Row组件水平排列减少按钮、数量文本和增加按钮,点击按钮可以调整商品数量。
  5. 商品详情部分使用Text组件显示商品的详细信息,通过\n实现多行文本。
  6. 底部操作栏固定在底部,包含"加入购物车"和"立即购买"两个按钮,使用layoutWeight属性使两个按钮平分宽度。

布局技巧

1. 垂直分割布局

使用ColumnSplit组件可以轻松实现上下分割的布局效果,适合商品详情页这种需要展示图片和详细信息的场景。通过设置子组件的高度比例,可以控制上下两部分的大小关系。

2. 嵌套布局

在本案例中,我们使用了多层嵌套的布局结构:

  • 最外层是ColumnSplit,将界面分为上下两部分
  • 上部分使用Column包含Swiper和指示器
  • 下部分使用Column包含Scroll和底部操作栏
  • Scroll内部使用Column垂直排列各个信息区块
  • 每个信息区块内部又使用RowColumnFlex等组件进行布局

这种嵌套布局的方式使得界面结构清晰,各个部分的职责明确。

3. 滚动与固定结合

在商品信息区,我们使用了Scroll组件使内容可滚动,同时将底部操作栏固定在底部不随内容滚动。这种布局方式在移动应用中非常常见,可以在有限的屏幕空间内展示更多内容,同时保持关键操作按钮始终可见。

4. 弹性布局

在商品规格选择部分,我们使用了Flex组件创建一个弹性布局,并设置wrap: FlexWrap.Wrap使得当一行放不下所有选项时会自动换行。这种布局方式适合展示数量不确定的选项,可以根据屏幕宽度自动调整排列。

交互实现

1. 图片轮播

通过Swiper组件实现图片轮播,并通过onChange事件更新当前显示的图片索引:

Swiper()
    // ...
    .onChange((index: number) => {
        this.currentImageIndex = index
    })

2. 规格选择

通过Button组件的onClick事件实现规格选择,点击按钮时更新selectedSize状态:

Button(size)
    // ...
    .onClick(() => this.selectedSize = size)

3. 数量调整

通过Button组件的onClick事件实现数量调整,点击"+"按钮增加数量,点击"-"按钮减少数量:

Button('-')
    // ...
    .onClick(() => {
        if (this.quantity > 1) this.quantity--
    })

Button('+')
    // ...
    .onClick(() => this.quantity++)

4. 收藏切换

通过Button组件的onClick事件实现收藏状态切换,点击按钮时切换isFavorite状态:

Button(this.isFavorite ? $r('app.media.01') : $r('app.media.02'))
    // ...
    .onClick(() => this.isFavorite = !this.isFavorite)

样式设计

1. 颜色搭配

在本案例中,我们使用了以下颜色:

  • 主色调:#ff4757(红色),用于价格、选中状态和"立即购买"按钮
  • 次要色调:#ff9500(橙色),用于"加入购物车"按钮
  • 背景色:#f5f5f5(浅灰色),用于未选中的规格按钮
  • 文本颜色:#333333(深灰色)用于主要文本,#666666(中灰色)用于次要文本

这种颜色搭配符合电商应用的视觉风格,突出重要信息和操作按钮。

2. 间距设置

在布局中,我们使用了一致的间距设置:

  • 整体内容区域设置了20的内边距
  • 各个区块之间设置了20的下边距
  • 按钮之间设置了10的间距

这种一致的间距设置使得界面看起来整洁有序。

3. 字体大小

在本案例中,我们使用了不同的字体大小来区分不同层级的信息:

  • 商品标题:20
  • 价格:24
  • 区块标题:16-18
  • 正文内容:14

这种字体大小的层次设置使得用户可以快速识别重要信息。

总结

在本教程中,我们学习了如何使用HarmonyOS NEXT的ColumnSplit组件构建一个电商商品详情页。通过垂直分割布局,我们将界面分为商品图片区域和商品信息区域,使得界面结构清晰,信息展示合理。

我们还学习了如何使用Swiper组件实现图片轮播,如何使用Flex组件创建弹性布局,如何使用Scroll组件实现内容滚动,以及如何通过状态变量和事件处理实现各种交互功能。

通过本教程,你应该能够理解垂直分割布局在电商商品详情页中的应用,并能够根据自己的需求进行定制和扩展。

收藏00

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