39.[HarmonyOS NEXT Row案例七] 打造精美商品列表项:图文混排与多行文本的艺术
项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
1. 概述
在电商应用和内容展示类应用中,商品列表项是一个核心UI组件,它需要在有限的空间内高效展示商品图片、标题、价格等多种信息。本教程将详细讲解如何使用HarmonyOS NEXT的Row组件结合Column组件创建一个精美的商品列表项,实现图文混排与多行文本的完美展示。
2. 商品列表项的设计原则
在设计商品列表项时,需要考虑以下几个关键原则:
- 信息层级:突出重要信息(如商品名称、价格),次要信息(如描述、评分)可以用较小字体或浅色显示。
- 空间利用:合理利用有限空间,确保关键信息完整显示。
- 视觉平衡:图片与文本区域的比例要协调,通常图片占据固定宽度,文本区域自适应。
- 一致性:在整个应用中保持商品列表项的一致样式,提升用户体验。
- 可点击区域:整个列表项通常是可点击的,要确保点击区域足够大,便于用户操作。
3. 案例分析:商品列表项
本案例展示了如何创建一个包含商品图片、标题、描述和价格的商品列表项,通过Row组件实现图文混排,通过Column组件实现多行文本排列。
3.1 完整代码
@Component
export struct ProductItem {
private productName: string = '超级好用的智能手表'
private description: string = '这是一款功能强大的智能手表,支持心率监测、血氧检测、睡眠分析等多种健康功能,同时还具备防水、长续航等特点。'
private price: string = '¥1299'
build() {
Row() {
// 商品图片
Image($r('app.media.product_image'))
.width(100)
.height(100)
.objectFit(ImageFit.Cover)
.borderRadius(8)
// 商品信息
Column() {
Text(this.productName)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 4 })
Text(this.description)
.fontSize(14)
.fontColor('#666666')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ bottom: 8 })
Text(this.price)
.fontSize(16)
.fontColor('#FF6B00')
.fontWeight(FontWeight.Bold)
}
.alignItems(HorizontalAlign.Start)
.flexGrow(1)
.margin({ left: 12 })
}
.width('100%')
.backgroundColor('#FFFFFF')
.borderRadius(12)
.padding(16)
}
}
3.2 代码详解
3.2.1 组件声明与数据定义
@Component
export struct ProductItem {
private productName: string = '超级好用的智能手表'
private description: string = '这是一款功能强大的智能手表,支持心率监测、血氧检测、睡眠分析等多种健康功能,同时还具备防水、长续航等特点。'
private price: string = '¥1299'
这部分代码声明了一个名为ProductItem
的自定义组件,并定义了三个私有属性:
属性 | 类型 | 说明 |
---|---|---|
productName | string | 商品名称 |
description | string | 商品描述 |
price | string | 商品价格 |
在实际应用中,这些数据通常会通过组件的构造参数传入,而不是硬编码在组件内部。
3.2.2 外层Row容器设置
Row() {
// 子组件
}
.width('100%')
.backgroundColor('#FFFFFF')
.borderRadius(12)
.padding(16)
这部分代码创建了一个Row容器,作为商品列表项的根容器。Row容器的属性设置如下:
属性 | 值 | 说明 |
---|---|---|
width | '100%' | 容器宽度为父容器的100% |
backgroundColor | '#FFFFFF' | 设置背景色为白色 |
borderRadius | 12 | 设置边框圆角为12vp |
padding | 16 | 设置内边距为16vp |
这些设置确保了商品列表项有足够的空间和适当的内边距,白色背景和圆角边框使列表项在视觉上更加突出。
3.2.3 商品图片设置
Image($r('app.media.product_image'))
.width(100)
.height(100)
.objectFit(ImageFit.Cover)
.borderRadius(8)
这部分代码创建了一个Image组件,显示商品图片。Image组件的属性设置如下:
属性 | 值 | 说明 |
---|---|---|
资源 | $r('app.media.product_image') | 使用应用资源中的图片 |
width | 100 | 设置宽度为100vp |
height | 100 | 设置高度为100vp |
objectFit | ImageFit.Cover | 设置图片填充模式为覆盖,保持宽高比 |
borderRadius | 8 | 设置边框圆角为8vp |
这些设置确保了商品图片有固定的尺寸和适当的圆角,ImageFit.Cover
模式确保图片能够完全覆盖指定区域,同时保持原始宽高比,避免图片变形。
3.2.4 商品信息Column容器设置
Column() {
// 子组件
}
.alignItems(HorizontalAlign.Start)
.flexGrow(1)
.margin({ left: 12 })
这部分代码创建了一个Column容器,用于垂直排列商品信息。Column容器的属性设置如下:
属性 | 值 | 说明 |
---|---|---|
alignItems | HorizontalAlign.Start | 子组件在水平方向上左对齐 |
flexGrow | 1 | 设置弹性增长系数为1,占据Row容器中的剩余空间 |
margin | { left: 12 } | 设置左侧外边距为12vp,与商品图片保持间距 |
这些设置确保了商品信息区域能够自适应占据除图片外的所有空间,并且内部的文本左对齐,与图片保持适当的间距。
3.2.5 商品名称设置
Text(this.productName)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 4 })
这部分代码创建了一个Text组件,显示商品名称。Text组件的属性设置如下:
属性 | 值 | 说明 |
---|---|---|
fontSize | 16 | 设置字体大小为16fp |
fontWeight | FontWeight.Bold | 设置字体粗细为粗体 |
margin | { bottom: 4 } | 设置底部外边距为4vp |
这些设置使商品名称以粗体显示,突出其重要性,并与下方的商品描述保持适当的间距。
3.2.6 商品描述设置
Text(this.description)
.fontSize(14)
.fontColor('#666666')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ bottom: 8 })
这部分代码创建了一个Text组件,显示商品描述。Text组件的属性设置如下:
属性 | 值 | 说明 |
---|---|---|
fontSize | 14 | 设置字体大小为14fp |
fontColor | '#666666' | 设置字体颜色为深灰色 |
maxLines | 2 | 设置最大显示行数为2行 |
textOverflow | { overflow: TextOverflow.Ellipsis } | 设置文本溢出时显示省略号 |
margin | { bottom: 8 } | 设置底部外边距为8vp |
这些设置使商品描述以较小的字体和较浅的颜色显示,表明其次要地位,同时限制最大显示行数为2行,超出部分显示省略号,避免占用过多空间。
3.2.7 商品价格设置
Text(this.price)
.fontSize(16)
.fontColor('#FF6B00')
.fontWeight(FontWeight.Bold)
这部分代码创建了一个Text组件,显示商品价格。Text组件的属性设置如下:
属性 | 值 | 说明 |
---|---|---|
fontSize | 16 | 设置字体大小为16fp |
fontColor | '#FF6B00' | 设置字体颜色为橙色 |
fontWeight | FontWeight.Bold | 设置字体粗细为粗体 |
这些设置使商品价格以橙色粗体显示,突出其重要性,橙色是电商应用中常用的价格颜色,能够吸引用户注意。
4. 图文混排的实现原理
在HarmonyOS NEXT中,图文混排主要通过Row容器实现。Row容器是一个水平排列子组件的容器,可以将图片和文本区域放在同一行中,实现图文混排的效果。
4.1 Row容器的工作原理
Row容器默认将子组件水平排列,子组件之间的间距和对齐方式可以通过属性设置。在本案例中,Row容器包含两个子组件:一个Image组件和一个Column组件,它们被水平排列,形成图文混排的布局。
4.2 弹性布局的应用
在图文混排中,通常需要图片区域固定宽度,文本区域自适应占据剩余空间。这可以通过弹性布局实现:
Image(...).width(100) // 图片固定宽度
Column(...).flexGrow(1) // 文本区域自适应占据剩余空间
flexGrow
属性指定了弹性增长系数,当设置为1时,表示该组件会自动扩展以占据父容器中的剩余空间。在本案例中,Column容器的flexGrow
设置为1,使其能够自适应占据除图片外的所有空间。
5. 多行文本的实现原理
在商品列表项中,商品描述通常需要显示多行文本,并且在空间有限时需要截断显示。这可以通过Text组件的maxLines
和textOverflow
属性实现。
5.1 maxLines属性
maxLines
属性指定了Text组件最多显示的行数:
.maxLines(2) // 最多显示2行
当文本内容超过指定的行数时,超出部分会被截断。
5.2 textOverflow属性
textOverflow
属性指定了文本溢出时的处理方式:
.textOverflow({ overflow: TextOverflow.Ellipsis }) // 溢出时显示省略号
TextOverflow.Ellipsis
表示当文本溢出时显示省略号,这是一种常见的处理方式,能够提示用户还有更多内容未显示。
5.3 多行文本的布局考虑
在设计多行文本时,需要考虑以下几点:
- 行高:适当的行高可以提升多行文本的可读性。
- 字体大小:较小的字体可以在有限空间内显示更多内容。
- 截断方式:根据内容的重要性选择合适的截断方式。
- 交互方式:考虑是否需要提供"查看更多"的交互方式。
6. 商品列表项的样式优化
为了提升商品列表项的视觉效果和用户体验,我们可以进行以下优化:
6.1 阴影效果
添加阴影可以使商品列表项在视觉上更加突出:
.shadow({
radius: 8,
color: 'rgba(0, 0, 0, 0.1)',
offsetX: 0,
offsetY: 2
})
这些设置创建了一个轻微的阴影效果,使商品列表项看起来像是悬浮在背景上,增强了层次感。
6.2 点击效果
添加点击效果可以提升用户交互体验:
.onClick(() => {
// 处理点击事件
})
.stateStyles({
pressed: {
opacity: 0.8,
backgroundColor: '#F8F8F8'
}
})
这些设置使商品列表项在被点击时有明显的视觉反馈,提示用户点击操作已被识别。
6.3 边框效果
添加边框可以使商品列表项更加清晰:
.border({
width: 1,
color: '#EEEEEE',
radius: 12
})
这些设置添加了一个浅色边框,使商品列表项的边界更加清晰,特别是在白色背景上。
7. 商品列表项的交互优化
为了提升商品列表项的交互体验,我们可以添加以下功能:
7.1 收藏功能
在商品列表项中添加收藏按钮,使用户可以快速收藏感兴趣的商品:
Row() {
// 商品图片和信息
// 收藏按钮
Column() {
Image(this.isFavorite ? $r('app.media.ic_favorite_filled') : $r('app.media.ic_favorite'))
.width(24)
.height(24)
.onClick((event) => {
this.isFavorite = !this.isFavorite
event.stopPropagation() // 阻止事件冒泡,避免触发列表项的点击事件
})
}
.justifyContent(FlexAlign.Start)
.alignSelf(ItemAlign.Start)
}
在这个优化版本中,我们在Row容器的最右侧添加了一个收藏按钮,用户可以点击按钮收藏或取消收藏商品,同时阻止事件冒泡,避免触发列表项的点击事件。
7.2 数量选择
在商品列表项中添加数量选择功能,使用户可以直接调整购买数量:
Row() {
// 商品图片和信息
// 价格和数量选择
Row() {
Text(this.price)
.fontSize(16)
.fontColor('#FF6B00')
.fontWeight(FontWeight.Bold)
Blank()
Row() {
Button('-')
.width(24)
.height(24)
.fontSize(14)
.onClick((event) => {
if (this.quantity > 1) {
this.quantity--
}
event.stopPropagation()
})
Text(this.quantity.toString())
.width(36)
.textAlign(TextAlign.Center)
.fontSize(14)
Button('+')
.width(24)
.height(24)
.fontSize(14)
.onClick((event) => {
this.quantity++
event.stopPropagation()
})
}
.height(24)
}
.width('100%')
}
在这个优化版本中,我们在价格旁边添加了数量选择功能,用户可以点击"+"或"-"按钮调整购买数量,同时阻止事件冒泡,避免触发列表项的点击事件。
8. 商品列表项的扩展功能
基于本案例的基本结构,我们可以扩展更多功能:
8.1 评分和评论数
在商品描述下方添加评分和评论数信息:
Row() {
// 评分
Row() {
ForEach([1, 2, 3, 4, 5], (item) => {
Image(item <= this.rating ? $r('app.media.ic_star_filled') : $r('app.media.ic_star'))
.width(16)
.height(16)
})
}
Text(this.rating.toString())
.fontSize(14)
.fontColor('#FF6B00')
.margin({ left: 4 })
Text(`(${this.commentCount}条评论)`)
.fontSize(14)
.fontColor('#999999')
.margin({ left: 8 })
}
.margin({ bottom: 8 })
在这个扩展功能中,我们添加了评分和评论数信息,使用星星图标表示评分,并显示评论数量,提供更多商品信息。
8.2 标签和促销信息
在商品名称下方添加标签和促销信息:
Row({ space: 8 }) {
Text('新品')
.fontSize(12)
.fontColor('#FF6B00')
.backgroundColor('#FFF0E6')
.borderRadius(4)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
Text('限时促销')
.fontSize(12)
.fontColor('#FF0000')
.backgroundColor('#FFEBEB')
.borderRadius(4)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
}
.margin({ bottom: 8 })
在这个扩展功能中,我们添加了标签和促销信息,使用不同的背景色和文字颜色区分不同类型的信息,提供更多商品特点和促销信息。
9. 商品列表项组件的封装与复用
为了提高代码复用性,可以将商品列表项封装为一个独立的组件:
@Component
export struct ProductItem {
@Prop productName: string
@Prop description: string
@Prop price: string
@Prop imageUrl: string
@Prop rating: number = 0
@Prop commentCount: number = 0
@State isFavorite: boolean = false
@State quantity: number = 1
onItemClick?: (productName: string) => void
build() {
Row() {
// 商品图片
Image(this.imageUrl)
.width(100)
.height(100)
.objectFit(ImageFit.Cover)
.borderRadius(8)
// 商品信息
Column() {
Text(this.productName)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 4 })
Text(this.description)
.fontSize(14)
.fontColor('#666666')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ bottom: 8 })
// 评分和评论数
if (this.rating > 0) {
Row() {
// 评分
Row() {
ForEach([1, 2, 3, 4, 5], (item) => {
Image(item <= this.rating ? $r('app.media.ic_star_filled') : $r('app.media.ic_star'))
.width(16)
.height(16)
})
}
Text(this.rating.toString())
.fontSize(14)
.fontColor('#FF6B00')
.margin({ left: 4 })
Text(`(${this.commentCount}条评论)`)
.fontSize(14)
.fontColor('#999999')
.margin({ left: 8 })
}
.margin({ bottom: 8 })
}
// 价格和收藏
Row() {
Text(this.price)
.fontSize(16)
.fontColor('#FF6B00')
.fontWeight(FontWeight.Bold)
Blank()
Image(this.isFavorite ? $r('app.media.ic_favorite_filled') : $r('app.media.ic_favorite'))
.width(24)
.height(24)
.onClick((event) => {
this.isFavorite = !this.isFavorite
event.stopPropagation()
})
}
.width('100%')
}
.alignItems(HorizontalAlign.Start)
.flexGrow(1)
.margin({ left: 12 })
}
.width('100%')
.backgroundColor('#FFFFFF')
.borderRadius(12)
.padding(16)
.shadow({
radius: 8,
color: 'rgba(0, 0, 0, 0.1)',
offsetX: 0,
offsetY: 2
})
.onClick(() => {
if (this.onItemClick) {
this.onItemClick(this.productName)
}
})
.stateStyles({
pressed: {
opacity: 0.8,
backgroundColor: '#F8F8F8'
}
})
}
}
然后在应用中使用这个组件:
@Entry
@Component
struct ProductListPage {
@State products: Array<{
name: string,
description: string,
price: string,
imageUrl: string,
rating: number,
commentCount: number
}> = [
{
name: '超级好用的智能手表',
description: '这是一款功能强大的智能手表,支持心率监测、血氧检测、睡眠分析等多种健康功能,同时还具备防水、长续航等特点。',
price: '¥1299',
imageUrl: $r('app.media.product_image_1'),
rating: 4.5,
commentCount: 1234
},
{
name: '高性能游戏笔记本电脑',
description: '搭载最新处理器和显卡,提供卓越的游戏体验和工作效率。',
price: '¥8999',
imageUrl: $r('app.media.product_image_2'),
rating: 4.8,
commentCount: 567
}
// 更多商品...
]
build() {
Column({ space: 12 }) {
// 标题
Text('商品列表')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.padding({ left: 16, top: 16, bottom: 8 })
// 商品列表
List() {
ForEach(this.products, (product) => {
ListItem() {
ProductItem({
productName: product.name,
description: product.description,
price: product.price,
imageUrl: product.imageUrl,
rating: product.rating,
commentCount: product.commentCount,
onItemClick: (productName) => {
// 处理商品点击事件
console.info('点击商品:' + productName)
}
})
}
.padding({ left: 16, right: 16, bottom: 8 })
})
}
.width('100%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}
10. 总结
本教程详细讲解了如何使用HarmonyOS NEXT的Row组件结合Column组件创建精美的商品列表项,实现图文混排与多行文本的完美展示。通过本案例,我们学习了:
- 商品列表项的设计原则
- Row组件在图文混排中的应用
- Column组件在多行文本排列中的应用
- 弹性布局在商品列表项中的应用
- 多行文本的实现原理和截断处理
- 商品列表项的样式优化技巧
- 商品列表项的交互优化方法
- 商品列表项的扩展功能
- 商品列表项组件的封装与复用
掌握这些知识点后,你可以设计出美观、实用的商品列表项,提升应用的用户体验。在实际开发中,可以根据具体需求调整商品列表项的样式、布局和功能,创建符合应用设计风格的商品展示界面。
- 0回答
- 3粉丝
- 0关注
- 42.[HarmonyOS NEXT Row案例十] 精美图文混排卡片:左图标与右文本的完美结合
- 36.[HarmonyOS NEXT Row案例四] 打造精美图标按钮:垂直对齐与视觉平衡的艺术
- 41. [HarmonyOS NEXT Row案例九] 打造流畅可滑动列表项:滑动操作按钮的高级实现
- 38.[HarmonyOS NEXT Row案例六] 打造流畅水平滚动标签列表:Row与Scroll的完美结合
- 34. [HarmonyOS NEXT Row案例二] 打造响应式图文导航项:设备适配与弹性空间的完美结合
- 39. HarmonyOS NEXT Layout布局组件系统详解(六):偏移功能实现
- 28.[HarmonyOS NEXT Column案例七(上)] 多层嵌套布局:打造结构清晰的详情页面
- 35.[HarmonyOS NEXT Row案例三] 打造高效表单输入框:标签与输入框的弹性空间分配技巧
- 13.HarmonyOS NEXT流式卡片列表实现指南:Flex多行布局详解
- 25.[HarmonyOS NEXT Column案例五(下)] 精细化列表项设计:多层嵌套布局与视觉层次构建
- 161.HarmonyOS NEXT系列教程之列表交换组件列表项交互实现
- 176.HarmonyOS NEXT系列教程之列表交换组件列表项操作实现
- 40. [HarmonyOS NEXT Row案例八] 打造专业数据统计卡片:两端对齐与响应式图标的完美结合
- 43.[HarmonyOS NEXT Row案例十一] 构建智能分页控件:Row组件实现页码与翻页按钮的完美结合
- 33. [HarmonyOS NEXT Row案例一(下)] 深入理解Row组件与按钮组布局技巧