182.[HarmonyOS NEXT 实战案例九:Grid] 电商网格布局进阶篇:打造高级交互与视觉体验
[HarmonyOS NEXT 实战案例九:Grid] 电商网格布局进阶篇:打造高级交互与视觉体验
项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
1. 电商网格布局进阶概述
在基础篇中,我们学习了如何使用 HarmonyOS NEXT 的 Grid 和 GridItem 组件创建基本的电商商品展示页面。在本篇教程中,我们将深入探讨更高级的电商网格布局技术,包括高级交互设计、动态布局调整、高级样式与视觉效果等,帮助开发者打造出更具吸引力和交互性的电商应用。
1.1 进阶特性概览
特性 | 描述 |
---|---|
高级交互设计 | 商品详情对话框、购物车浮层、手势交互 |
动态布局调整 | 响应式列数、动态卡片大小 |
高级样式与视觉效果 | 卡片样式变体、动画效果 |
高级数据处理 | 多维度筛选、复杂排序算法 |
购物车管理 | 购物车浮层、数量调整、总价计算 |
2. 高级交互设计
2.1 商品详情对话框
商品详情对话框是电商应用中的核心交互元素,它允许用户在不离开当前页面的情况下查看商品的详细信息。
@Builder
ProductDetailDialog() {
if (this.selectedProduct) {
Column() {
// 商品图片
Image(this.selectedProduct.image)
.width('100%')
.height(300)
.objectFit(ImageFit.Cover)
.borderRadius({ topLeft: 16, topRight: 16 })
// 商品信息
Column() {
// 价格和标签
Row() {
Column() {
Row() {
Text(`¥${this.selectedProduct.price}`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FF3B30')
if (this.selectedProduct.originalPrice) {
Text(`¥${this.selectedProduct.originalPrice}`)
.fontSize(14)
.fontColor('#999999')
.decoration({ type: TextDecorationType.LineThrough })
.margin({ left: 8 })
}
if (this.selectedProduct.discount) {
Text(`${this.selectedProduct.discount}折`)
.fontSize(12)
.fontColor('#FFFFFF')
.backgroundColor('#FF3B30')
.borderRadius(4)
.padding({ left: 4, right: 4, top: 2, bottom: 2 })
.margin({ left: 8 })
}
}
if (this.selectedProduct.isFreeShipping) {
Text('包邮')
.fontSize(12)
.fontColor('#007AFF')
.border({ width: 1, color: '#007AFF' })
.borderRadius(4)
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
.margin({ top: 4 })
}
}
.alignItems(HorizontalAlign.Start)
Blank()
Column() {
if (this.selectedProduct.isHot) {
Text('热销')
.fontSize(12)
.fontColor('#FFFFFF')
.backgroundColor('#FF9500')
.borderRadius(4)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.margin({ bottom: 4 })
}
if (this.selectedProduct.isNew) {
Text('新品')
.fontSize(12)
.fontColor('#FFFFFF')
.backgroundColor('#34C759')
.borderRadius(4)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
}
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
.margin({ bottom: 16 })
// 商品名称
Text(this.selectedProduct.name)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.width('100%')
.textAlign(TextAlign.Start)
.margin({ bottom: 12 })
// 商品描述
Text(this.selectedProduct.description)
.fontSize(14)
.fontColor('#666666')
.width('100%')
.textAlign(TextAlign.Start)
.margin({ bottom: 16 })
// 评分和销量
Row() {
Row() {
ForEach([1,2,3,4,5], (star:number) => {
Image(star <= this.selectedProduct.rating ? $r('app.media.star_filled') : $r('app.media.star_outline'))
.width(16)
.height(16)
.fillColor(star <= this.selectedProduct.rating ? '#FFD700' : '#E0E0E0')
})
Text(`${this.selectedProduct.rating}`)
.fontSize(14)
.fontColor('#666666')
.margin({ left: 4 })
}
Text(`${this.selectedProduct.reviewCount}条评价`)
.fontSize(14)
.fontColor('#666666')
.margin({ left: 16, right: 16 })
Text(`${this.formatNumber(this.selectedProduct.salesCount)}已售`)
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.margin({ bottom: 16 })
// 商品标签
Row() {
Text('标签:')
.fontSize(14)
.fontColor('#666666')
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.selectedProduct.tags, (tag:string) => {
Text(tag)
.fontSize(12)
.fontColor('#666666')
.backgroundColor('#F0F0F0')
.borderRadius(12)
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.margin({ right: 8, bottom: 8 })
})
}
}
.width('100%')
.margin({ bottom: 16 })
// 操作按钮
Row() {
Button() {
Row() {
Image($r('app.media.cart_add'))
.width(20)
.height(20)
.fillColor('#FFFFFF')
.margin({ right: 8 })
Text('加入购物车')
.fontSize(16)
.fontColor('#FFFFFF')
}
}
.backgroundColor('#007AFF')
.borderRadius(24)
.width('48%')
.height(48)
.onClick(() => {
this.addToCart(this.selectedProduct.id)
this.showProductDetail = false
})
Button() {
Text('立即购买')
.fontSize(16)
.fontColor('#FFFFFF')
}
.backgroundColor('#FF3B30')
.borderRadius(24)
.width('48%')
.height(48)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.padding(20)
.alignItems(HorizontalAlign.Start)
}
.width('95%')
.backgroundColor('#FFFFFF')
.borderRadius(16)
.maxHeight('85%')
}
}
商品详情对话框的设计要点:
-
层次分明的信息结构:从上到下依次展示商品图片、价格信息、商品名称、描述、评分销量、标签和操作按钮,符合用户的阅读习惯和信息获取优先级。
-
丰富的视觉标识:使用不同的颜色和样式标识热销、新品、包邮等特殊属性,增强信息的可识别性。
-
灵活的布局:使用 Row 和 Column 组件灵活组合,实现复杂的信息布局。
-
明确的操作区域:底部提供"加入购物车"和"立即购买"两个主要操作按钮,按钮大小适中,易于点击。
-
滚动适配:通过 maxHeight 属性限制对话框最大高度,确保在内容过多时可以滚动查看。
2.2 购物车浮层
购物车浮层允许用户快速查看已添加的商品,并进行结算操作。
// 购物车浮层
Column() {
// 顶部标题栏
Row() {
Text('购物车')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Blank()
Button() {
Image($r('app.media.close'))
.width(24)
.height(24)
.fillColor('#999999')
}
.backgroundColor('transparent')
.width(36)
.height(36)
.onClick(() => {
this.showCart = false
})
}
.width('100%')
.padding({ left: 20, right: 20, top: 20, bottom: 12 })
// 购物车为空提示
if (this.cartItems.length === 0) {
Column() {
Image($r('app.media.empty_cart'))
.width(120)
.height(120)
.margin({ bottom: 16, top: 40 })
Text('购物车还是空的')
.fontSize(16)
.fontColor('#999999')
Button('去选购')
.fontSize(16)
.fontColor('#FFFFFF')
.backgroundColor('#007AFF')
.borderRadius(24)
.width(160)
.height(48)
.margin({ top: 24 })
.onClick(() => {
this.showCart = false
})
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
} else {
// 购物车商品列表
List() {
ForEach(this.cartItems, (item:CateItem) => {
ListItem() {
Row() {
// 商品图片
Image(this.products.find(p => p.id === item.productId)?.image)
.width(80)
.height(80)
.objectFit(ImageFit.Cover)
.borderRadius(8)
// 商品信息
Column() {
Text(this.products.find(p => p.id === item.productId)?.name)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width('100%')
.textAlign(TextAlign.Start)
.margin({ bottom: 8 })
// 价格
Row() {
Text(`¥${this.products.find(p => p.id === item.productId)?.price}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FF3B30')
Blank()
// 数量调整
Row() {
Button('-')
.fontSize(16)
.fontColor('#333333')
.backgroundColor('#F0F0F0')
.width(28)
.height(28)
.borderRadius(14)
.onClick(() => {
if (item.quantity > 1) {
item.quantity--
} else {
this.cartItems = this.cartItems.filter(i => i.productId !== item.productId)
}
})
Text(item.quantity.toString())
.fontSize(14)
.fontColor('#333333')
.width(40)
.textAlign(TextAlign.Center)
Button('+')
.fontSize(16)
.fontColor('#333333')
.backgroundColor('#F0F0F0')
.width(28)
.height(28)
.borderRadius(14)
.onClick(() => {
const product = this.products.find(p => p.id === item.productId)
if (product && item.quantity < product.stock) {
item.quantity++
}
})
}
}
.width('100%')
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ left: 12 })
}
.width('100%')
.padding({ top: 12, bottom: 12 })
}
})
}
.width('100%')
.layoutWeight(1)
.padding({ left: 20, right: 20 })
// 底部结算栏
Row() {
Column() {
Text('总计:')
.fontSize(14)
.fontColor('#666666')
Text(`¥${this.getCartTotal().toFixed(2)}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#FF3B30')
}
.alignItems(HorizontalAlign.Start)
Blank()
Button('去结算')
.fontSize(16)
.fontColor('#FFFFFF')
.backgroundColor('#FF3B30')
.borderRadius(24)
.width(120)
.height(48)
}
.width('100%')
.padding(20)
.border({ width: { top: 1 }, color: { top: '#F0F0F0' } })
}
}
.width('100%')
.height('70%')
.backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 16, topRight: 16 })
.position({ x: 0, y: '30%' })
购物车浮层的设计要点:
-
清晰的标题和关闭按钮:顶部显示标题和关闭按钮,方便用户理解当前界面和退出操作。
-
空状态处理:当购物车为空时,显示友好的提示和引导用户去选购的按钮。
-
商品列表:使用 List 组件展示购物车中的商品,每个商品项包含图片、名称、价格和数量调整控件。
-
数量调整:提供增加和减少按钮,允许用户调整商品数量,并处理边界情况(数量为1时减少则移除商品,数量达到库存上限时禁止增加)。
-
结算区域:底部显示总价和结算按钮,明确引导用户完成购买流程。
-
半屏浮层:使用 position 属性将浮层定位在屏幕下半部分,保留上下文信息,提升用户体验。
3. 动态布局调整
3.1 响应式列数
在不同屏幕尺寸下,我们可以动态调整商品网格的列数,以提供最佳的视觉体验。
@State gridColumns: number = 2
onPortrait(mediaQueryResult: MediaQueryResult) {
if (mediaQueryResult.matches) {
// 竖屏模式
this.gridColumns = 2
}
}
onLandscape(mediaQueryResult: MediaQueryResult) {
if (mediaQueryResult.matches) {
// 横屏模式
this.gridColumns = 3
}
}
aboutToAppear() {
// 监听屏幕方向变化
mediaQuery.matchMediaSync('(orientation: portrait)').on('change', this.onPortrait)
mediaQuery.matchMediaSync('(orientation: landscape)').on('change', this.onLandscape)
// 初始化列数
if (mediaQuery.matchMediaSync('(orientation: landscape)').matches) {
this.gridColumns = 3
} else {
this.gridColumns = 2
}
}
aboutToDisappear() {
// 移除监听器
mediaQuery.matchMediaSync('(orientation: portrait)').off('change', this.onPortrait)
mediaQuery.matchMediaSync('(orientation: landscape)').off('change', this.onLandscape)
}
然后在 Grid 组件中使用动态列数:
Grid() {
// GridItem 内容...
}
.columnsTemplate(this.gridColumns === 2 ? '1fr 1fr' : '1fr 1fr 1fr')
这样,当设备旋转或在不同尺寸的设备上运行时,网格布局会自动调整列数,提供最佳的视觉体验。
3.2 动态卡片大小
除了调整列数,我们还可以根据屏幕尺寸动态调整卡片的大小和内容布局。
@State cardImageHeight: number = 160
@State cardFontSize: number = 14
updateCardSize() {
const screenWidth = px2vp(window.getWindowWidth())
if (screenWidth > 600) {
// 大屏设备
this.cardImageHeight = 200
this.cardFontSize = 16
} else {
// 小屏设备
this.cardImageHeight = 160
this.cardFontSize = 14
}
}
aboutToAppear() {
// 初始化卡片大小
this.updateCardSize()
// 监听窗口大小变化
window.on('resize', () => {
this.updateCardSize()
})
}
aboutToDisappear() {
// 移除监听器
window.off('resize')
}
然后在 GridItem 中使用动态大小:
Image(product.image)
.width('100%')
.height(this.cardImageHeight)
.objectFit(ImageFit.Cover)
Text(product.name)
.fontSize(this.cardFontSize)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
这样,卡片的图片高度和文字大小会根据屏幕尺寸动态调整,提供更好的视觉体验。
4. 高级样式与视觉效果
4.1 卡片样式变体
为了增加视觉多样性,我们可以为不同类型的商品提供不同的卡片样式。
@Builder
ProductCard(product: EcommerceProduct) {
Column() {
// 基础卡片内容...
}
.width('100%')
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({
radius: 6,
color: 'rgba(0, 0, 0, 0.1)',
offsetX: 0,
offsetY: 2
})
// 根据商品类型应用不同的样式
.border(product.isHot ? {
width: 2,
color: '#FF9500',
style: BorderStyle.Solid
} : null)
.backgroundColor(product.isNew ? '#F8FFF8' : '#FFFFFF')
.onClick(() => {
this.selectedProduct = product
this.showProductDetail = true
})
}
这样,热销商品会有橙色边框,新品会有浅绿色背景,增加视觉差异化,帮助用户快速识别不同类型的商品。
4.2 高级动画效果
4.2.1 卡片加载动画
当商品数据正在加载时,我们可以添加骨架屏动画,提升用户体验。
@State isLoading: boolean = true
// 模拟数据加载
aboutToAppear() {
this.isLoading = true
setTimeout(() => {
this.isLoading = false
}, 1500)
}
@Builder
SkeletonCard() {
Column() {
// 图片占位
Row()
.width('100%')
.height(160)
.backgroundColor('#F0F0F0')
.borderRadius({ topLeft: 12, topRight: 12 })
.animation({
duration: 1500,
tempo: 1.0,
curve: Curve.Linear,
delay: 0,
iterations: -1,
playMode: PlayMode.Alternate
})
.opacity(this.isLoading ? 0.6 : 0.9)
// 信息占位
Column() {
// 标题占位
Row()
.width('80%')
.height(16)
.backgroundColor('#F0F0F0')
.borderRadius(8)
.margin({ bottom: 8 })
.animation({
duration: 1500,
tempo: 1.0,
curve: Curve.Linear,
delay: 200,
iterations: -1,
playMode: PlayMode.Alternate
})
.opacity(this.isLoading ? 0.6 : 0.9)
// 价格占位
Row()
.width('40%')
.height(16)
.backgroundColor('#F0F0F0')
.borderRadius(8)
.margin({ bottom: 8 })
.animation({
duration: 1500,
tempo: 1.0,
curve: Curve.Linear,
delay: 400,
iterations: -1,
playMode: PlayMode.Alternate
})
.opacity(this.isLoading ? 0.6 : 0.9)
// 评分占位
Row()
.width('60%')
.height(12)
.backgroundColor('#F0F0F0')
.borderRadius(6)
.margin({ bottom: 8 })
.animation({
duration: 1500,
tempo: 1.0,
curve: Curve.Linear,
delay: 600,
iterations: -1,
playMode: PlayMode.Alternate
})
.opacity(this.isLoading ? 0.6 : 0.9)
// 按钮占位
Row()
.width('100%')
.height(32)
.backgroundColor('#F0F0F0')
.borderRadius(16)
.animation({
duration: 1500,
tempo: 1.0,
curve: Curve.Linear,
delay: 800,
iterations: -1,
playMode: PlayMode.Alternate
})
.opacity(this.isLoading ? 0.6 : 0.9)
}
.padding(12)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({
radius: 6,
color: 'rgba(0, 0, 0, 0.1)',
offsetX: 0,
offsetY: 2
})
}
然后在 Grid 中根据加载状态显示不同的内容:
Grid() {
if (this.isLoading) {
// 显示骨架屏
ForEach([1, 2, 3, 4, 5, 6], (item) => {
GridItem() {
this.SkeletonCard()
}
})
} else {
// 显示实际商品
ForEach(this.getFilteredProducts(), (product: EcommerceProduct) => {
GridItem() {
this.ProductCard(product)
}
})
}
}
4.2.2 交互反馈动画
当用户点击商品卡片或操作按钮时,添加适当的动画反馈,提升交互体验。
@State cardScale: number = 1.0
// 在 GridItem 中添加点击动画
GridItem() {
Column() {
// 商品卡片内容...
}
.width('100%')
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({
radius: 6,
color: 'rgba(0, 0, 0, 0.1)',
offsetX: 0,
offsetY: 2
})
.scale({ x: this.cardScale, y: this.cardScale })
.animation({
duration: 100,
curve: Curve.FastOutSlowIn,
iterations: 1,
playMode: PlayMode.Normal
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.cardScale = 0.95
} else if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
this.cardScale = 1.0
}
})
.onClick(() => {
this.selectedProduct = product
this.showProductDetail = true
})
}
这样,当用户按下卡片时,卡片会有轻微的缩小效果,松开后恢复原状,提供更好的触觉反馈。
5. 高级数据处理
5.1 多维度筛选
在电商应用中,通常需要支持多维度的商品筛选,如分类、价格范围、品牌、评分等。
@State selectedBrands: string[] = []
@State minRating: number = 0
getFilteredProducts(): EcommerceProduct[] {
let filtered = this.products
// 分类过滤
if (this.selectedCategory !== '全部') {
filtered = filtered.filter(product => product.category === this.selectedCategory)
}
// 价格过滤
filtered = filtered.filter(product =>
product.price >= this.priceRange.min && product.price <= this.priceRange.max
)
// 品牌过滤
if (this.selectedBrands.length > 0) {
filtered = filtered.filter(product => this.selectedBrands.includes(product.brand))
}
// 评分过滤
filtered = filtered.filter(product => product.rating >= this.minRating)
// 搜索过滤
if (this.searchKeyword.trim() !== '') {
filtered = filtered.filter(product =>
product.name.includes(this.searchKeyword) ||
product.description.includes(this.searchKeyword) ||
product.brand.includes(this.searchKeyword) ||
product.tags.some(tag => tag.includes(this.searchKeyword))
)
}
// 排序
// ... 排序逻辑 ...
return filtered
}
5.2 复杂排序算法
电商应用中的排序通常不仅仅是简单的价格或销量排序,而是综合考虑多个因素的复杂算法。
getFilteredProducts(): EcommerceProduct[] {
let filtered = this.products
// ... 过滤逻辑 ...
// 排序
switch (this.sortBy) {
case '价格升序':
filtered.sort((a, b) => a.price - b.price)
break
case '价格降序':
filtered.sort((a, b) => b.price - a.price)
break
case '销量':
filtered.sort((a, b) => b.salesCount - a.salesCount)
break
case '评分':
filtered.sort((a, b) => b.rating - a.rating)
break
case '好评优先':
filtered.sort((a, b) => {
// 先按评分排序,评分相同则按评价数量排序
if (b.rating !== a.rating) {
return b.rating - a.rating
}
return b.reviewCount - a.reviewCount
})
break
case '新品优先':
filtered.sort((a, b) => {
// 新品排在前面,然后按上架时间排序
if (a.isNew !== b.isNew) {
return a.isNew ? -1 : 1
}
// 这里假设有上架时间字段,实际应用中需要根据具体数据模型调整
return b.id - a.id // 使用 ID 作为上架时间的近似值
})
break
default: // 综合
filtered.sort((a, b) => {
// 综合考虑多个因素的复杂排序算法
const scoreA = a.salesCount * 0.3 + a.rating * 1000 + a.reviewCount * 0.5 +
(a.isHot ? 500 : 0) + (a.isNew ? 300 : 0) + (a.isFreeShipping ? 100 : 0)
const scoreB = b.salesCount * 0.3 + b.rating * 1000 + b.reviewCount * 0.5 +
(b.isHot ? 500 : 0) + (b.isNew ? 300 : 0) + (b.isFreeShipping ? 100 : 0)
return scoreB - scoreA
})
}
return filtered
}
这个复杂的排序算法综合考虑了商品的销量、评分、评价数量、是否热销、是否新品、是否包邮等多个因素,为每个商品计算一个综合得分,然后按得分排序。
6. 购物车管理
6.1 购物车数据结构
购物车是电商应用的核心功能之一,需要高效的数据结构和操作方法。
// 购物车项目接口
interface CateItem {
productId: number; // 商品ID
quantity: number; // 数量
selected: boolean; // 是否选中
}
// 购物车状态
@State cartItems: CateItem[] = []
@State allSelected: boolean = true
6.2 购物车操作方法
// 添加到购物车
addToCart(productId: number, quantity: number = 1) {
const existingItem = this.cartItems.find(item => item.productId === productId)
if (existingItem) {
existingItem.quantity += quantity
} else {
this.cartItems.push({ productId, quantity, selected: true })
}
}
// 从购物车移除
removeFromCart(productId: number) {
this.cartItems = this.cartItems.filter(item => item.productId !== productId)
}
// 更新购物车商品数量
updateCartItemQuantity(productId: number, quantity: number) {
const item = this.cartItems.find(item => item.productId === productId)
if (item) {
item.quantity = quantity
}
}
// 切换商品选中状态
toggleCartItemSelection(productId: number) {
const item = this.cartItems.find(item => item.productId === productId)
if (item) {
item.selected = !item.selected
this.updateAllSelectedState()
}
}
// 切换全选状态
toggleAllSelection() {
this.allSelected = !this.allSelected
this.cartItems.forEach(item => {
item.selected = this.allSelected
})
}
// 更新全选状态
updateAllSelectedState() {
this.allSelected = this.cartItems.length > 0 && this.cartItems.every(item => item.selected)
}
// 获取选中的商品数量
getSelectedItemCount(): number {
return this.cartItems.filter(item => item.selected).reduce((total, item) => total + item.quantity, 0)
}
// 获取选中的商品总价
getSelectedTotal(): number {
return this.cartItems.filter(item => item.selected).reduce((total, item) => {
const product = this.products.find(p => p.id === item.productId)
return total + (product ? product.price * item.quantity : 0)
}, 0)
}
// 清空购物车
clearCart() {
this.cartItems = []
}
这些方法提供了完整的购物车管理功能,包括添加、移除、更新数量、选中/取消选中、全选/取消全选、计算总数量和总价等操作。
7. 总结
在本教程中,我们深入探讨了 HarmonyOS NEXT 电商网格布局的进阶特性,包括高级交互设计、动态布局调整、高级样式与视觉效果、高级数据处理和购物车管理等方面。
- 0回答
- 4粉丝
- 0关注
- 158.[HarmonyOS NEXT 实战案例一:Grid] 基础网格布局进阶篇:电商商品列表的交互与状态管理
- 183.[HarmonyOS NEXT 实战案例九:Grid] 电商网格布局高级篇:复杂场景与性能优化
- 159.[HarmonyOS NEXT 实战案例一:Grid] 基础网格布局高级篇:电商应用的复杂交互与动效实现
- [HarmonyOS NEXT 实战案例十五] 电商分类导航网格布局(进阶篇)
- 176.[HarmonyOS NEXT 实战案例七:Grid] 嵌套网格布局进阶篇:高级布局与交互技巧
- 181.[HarmonyOS NEXT 实战案例九:Grid] 电商网格布局基础篇:打造精美商品展示页面
- 164.[HarmonyOS NEXT 实战案例三:Grid] 不规则网格布局进阶篇:新闻应用高级布局与交互
- 157.[HarmonyOS NEXT 实战案例一:Grid] 基础网格布局:打造精美电商商品列表
- 161. [HarmonyOS NEXT 实战案例二:Grid] 照片相册网格布局:进阶篇
- 167.[HarmonyOS NEXT 实战案例四:Grid] 可滚动网格布局进阶篇
- 173.[HarmonyOS NEXT 实战案例六:Grid] 响应式网格布局 - 进阶篇
- 170.[HarmonyOS NEXT 实战案例五:Grid] 动态网格布局进阶篇
- 179.[HarmonyOS NEXT 实战案例八:Grid] 瀑布流网格布局进阶篇
- [HarmonyOS NEXT 实战案例:电商应用] 进阶篇 - 交互功能与状态管理
- [HarmonyOS NEXT 实战案例:电商应用] 进阶篇 - 交互功能与状态管理