HarmonyOS Next 跨端适配的电商购物应用开发 原创

SameX
发布于 2025-2-28 10:46
浏览
0收藏

电商应用的布局适配方案

商品展示的不同模式(网格布局 vs 列表布局)

在电商应用中,商品展示模式主要有网格布局和列表布局两种。网格布局以矩阵形式排列商品,能在有限空间内展示较多商品,适合快速浏览和比较商品。列表布局则是将商品依次纵向排列,每个商品信息展示更详细,注重商品详情的呈现。

小屏单列 vs 大屏多列的适配策略

小屏设备(如手机)屏幕空间有限,为保证商品信息清晰展示和操作便捷,商品列表通常采用单列布局。用户可以通过上下滑动轻松浏览商品。而大屏设备(如平板、PC)有更大的显示区域,采用多列布局能充分利用空间,提高信息展示效率。例如,在平板上可以展示双列商品,在 PC 上可以展示三列甚至更多列商品。

栅格布局 + 自适应布局 让商品卡片自动排列

栅格布局提供了一种灵活的网格系统,结合自适应布局可以实现商品卡片的自动排列。通过设置栅格列数和列间距,以及商品卡片的宽度占比,能够根据屏幕大小自动调整商品卡片的排列方式。例如,在小屏上,商品卡片宽度占比为 100%,实现单列布局;在大屏上,商品卡片宽度占比为 33.3%(三列布局)或 50%(双列布局)。

以下是示例代码:

import { BreakpointSystem, BreakPointType } from '../common/breakpointsystem';

@Entry
@Component
struct ProductDisplay {
    @State currentBreakpoint: string ='sm';
    @State products: Array<{ id: number, name: string, image: Resource }> = [
        { id: 1, name: '商品 1', image: $r('app.media.product1') },
        { id: 2, name: '商品 2', image: $r('app.media.product2') },
        // 更多商品...
    ];
    private breakpointSystem: BreakpointSystem = new BreakpointSystem();

    aboutToAppear() {
        this.breakpointSystem.register();
        this.breakpointSystem.onBreakpointChange((breakpoint: string) => {
            this.currentBreakpoint = breakpoint;
        });
    }

    aboutToDisappear() {
        this.breakpointSystem.unregister();
    }

    build() {
        GridRow({ breakpoints: { value: ['600vp', '840vp'], reference: BreakpointsReference.WindowSize } }) {
            ForEach(this.products, (product) => {
                GridCol({ span: { sm: 12, md: 6, lg: 4 } }) {
                    Column() {
                        Image(product.image).width('100%').aspectRatio(1).objectFit(ImageFit.Contain);
                        Text(product.name).fontSize(16).textAlign(TextAlign.Center);
                    }
                      .padding(10)
                      .backgroundColor('#FFFFFF')
                      .borderRadius(10)
                      .shadow({ color: '#00000020', offset: { x: 0, y: 2 }, blurRadius: 4 });
                }
            });
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

构建 HarmonyOS Next 购物界面

在大屏设备上,首页展示 3 栏模式(侧边导航栏 + 商品分类 + 商品详情)

大屏设备拥有充足的显示空间,采用三栏模式可以提供更丰富的信息展示和便捷的操作体验。侧边导航栏用于快速导航到不同的功能模块,商品分类栏展示各类商品分类,商品详情栏则显示当前选中商品的详细信息。

以下是示例代码:

@Entry
@Component
struct BigScreenShoppingPage {
    @State selectedCategory: string = '全部商品';
    @State productCategories: Array<string> = ['全部商品', '电子产品', '服装', '家居用品'];
    @State products: Array<{ id: number, name: string, image: Resource, category: string }> = [
        { id: 1, name: '商品 1', image: $r('app.media.product1'), category: '电子产品' },
        { id: 2, name: '商品 2', image: $r('app.media.product2'), category: '服装' },
        // 更多商品...
    ];
    @State selectedProductId: number | null = null;

    build() {
        SideBarContainer(SideBarContainerType.Embed) {
            // 侧边导航栏
            Column() {
                ForEach(['首页', '购物车', '个人中心'], (item) => {
                    Text(item).fontSize(18).onClick(() => {
                        // 导航逻辑
                    });
                });
            }
              .width('20%')
              .backgroundColor('#F1F3F5');

            Column() {
                // 商品分类栏
                GridRow() {
                    ForEach(this.productCategories, (category) => {
                        GridCol({ span: 3 }) {
                            Text(category).fontSize(16).onClick(() => {
                                this.selectedCategory = category;
                            });
                        }
                    });
                }

                // 商品列表栏
                GridRow() {
                    ForEach(this.products.filter(product => this.selectedCategory === '全部商品' || product.category === this.selectedCategory), (product) => {
                        GridCol({ span: 4 }) {
                            Column() {
                                Image(product.image).width('100%').aspectRatio(1).objectFit(ImageFit.Contain).onClick(() => {
                                    this.selectedProductId = product.id;
                                });
                                Text(product.name).fontSize(14).textAlign(TextAlign.Center);
                            }
                              .padding(10)
                              .backgroundColor('#FFFFFF')
                              .borderRadius(10)
                              .shadow({ color: '#00000020', offset: { x: 0, y: 2 }, blurRadius: 4 });
                        }
                    });
                }

                // 商品详情栏
                if (this.selectedProductId!== null) {
                    const selectedProduct = this.products.find(product => product.id === this.selectedProductId);
                    if (selectedProduct) {
                        Column() {
                            Image(selectedProduct.image).width('100%').aspectRatio(1).objectFit(ImageFit.Contain);
                            Text(selectedProduct.name).fontSize(20).fontWeight(500);
                            // 更多商品详情信息...
                        }
                          .width('30%')
                          .padding(20)
                          .backgroundColor('#FFFFFF')
                          .borderRadius(10)
                          .shadow({ color: '#00000020', offset: { x: 0, y: 2 }, blurRadius: 4 });
                    }
                }
            }
              .width('80%');
        }
          .sideBarWidth('20%')
          .showSideBar(true);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.

在小屏设备上,Tab 切换控制页面,商品列表以单列形式呈现

小屏设备屏幕空间有限,使用 Tab 切换控制页面可以简洁地展示不同功能模块。商品列表以单列形式呈现,方便用户单手操作和浏览。

以下是示例代码:

@Entry
@Component
struct SmallScreenShoppingPage {
    @State currentTab: number = 0;
    @State products: Array<{ id: number, name: string, image: Resource }> = [
        { id: 1, name: '商品 1', image: $r('app.media.product1') },
        { id: 2, name: '商品 2', image: $r('app.media.product2') },
        // 更多商品...
    ];

    build() {
        Column() {
            Tabs({ barPosition: BarPosition.End }) {
                TabContent() {
                    // 商品列表页
                    List() {
                        ForEach(this.products, (product) => {
                            ListItem() {
                                Column() {
                                    Image(product.image).width(100).height(100).objectFit(ImageFit.Contain);
                                    Text(product.name).fontSize(16);
                                }
                            }
                        });
                    }
                }
                .tabBar(
                    Column() {
                        Image($r('app.media.product_list_icon')).width(24).height(24);
                        Text('商品列表').fontSize(12);
                    }
                      .justifyContent(FlexAlign.Center).height('100%').width('100%')
                );
                TabContent() {
                    // 购物车页
                    // 购物车内容...
                }
                .tabBar(
                    Column() {
                        Image($r('app.media.shopping_cart_icon')).width(24).height(24);
                        Text('购物车').fontSize(12);
                    }
                      .justifyContent(FlexAlign.Center).height('100%').width('100%')
                );
                TabContent() {
                    // 个人中心页
                    // 个人中心内容...
                }
                .tabBar(
                    Column() {
                        Image($r('app.media.personal_center_icon')).width(24).height(24);
                        Text('个人中心').fontSize(12);
                    }
                      .justifyContent(FlexAlign.Center).height('100%').width('100%')
                );
            }
              .barMode(BarMode.Fixed)
              .barWidth('100%')
              .barHeight(56)
              .onChange((index: number) => {
                    this.currentTab = index;
                });
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.

使用 Swiper 组件优化 Banner 轮播,在不同屏幕下调整图片显示个数

Swiper 组件可以实现 Banner 轮播效果,通过设置 displayCount 属性可以在不同屏幕下调整图片显示个数。在小屏设备上显示 1 张图片,在大屏设备上显示 2 - 3 张图片。

以下是示例代码:

@Entry
@Component
struct BannerSwiper {
    @State currentBreakpoint: string ='sm';
    @State banners: Array<Resource> = [
        $r('app.media.banner1'),
        $r('app.media.banner2'),
        $r('app.media.banner3')
    ];
    private breakpointSystem: BreakpointSystem = new BreakpointSystem();

    aboutToAppear() {
        this.breakpointSystem.register();
        this.breakpointSystem.onBreakpointChange((breakpoint: string) => {
            this.currentBreakpoint = breakpoint;
        });
    }

    aboutToDisappear() {
        this.breakpointSystem.unregister();
    }

    build() {
        Swiper() {
            ForEach(this.banners, (banner) => {
                Image(banner).width('100%').height(200).objectFit(ImageFit.Cover);
            });
        }
          .autoPlay(true)
          .indicator(true)
          .displayCount(new BreakPointType({ sm: 1, md: 2, lg: 3 }).getValue(this.currentBreakpoint)!);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

购物体验优化与跨设备联动

动态调整商品卡片大小(使用 aspectRatio + constrainSize)

使用 aspectRatioconstrainSize 属性可以动态调整商品卡片的大小和比例。aspectRatio 用于固定商品卡片的宽高比,constrainSize 用于限制商品卡片的最大和最小尺寸。

以下是示例代码:

@Component
struct ProductCard {
    @Prop product: { id: number, name: string, image: Resource };

    build() {
        Column() {
            Image(this.product.image).width('100%').aspectRatio(1).constrainSize({ minWidth: 150, maxWidth: 250 }).objectFit(ImageFit.Contain);
            Text(this.product.name).fontSize(16).textAlign(TextAlign.Center);
        }
          .padding(10)
          .backgroundColor('#FFFFFF')
          .borderRadius(10)
          .shadow({ color: '#00000020', offset: { x: 0, y: 2 }, blurRadius: 4 });
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

结合自由窗口模式,支持窗口大小变化时自动切换 UI

通过监听窗口大小变化(断点监听),结合自适应和响应式布局,实现窗口大小变化时自动切换 UI。例如,当窗口从大屏模式变为小屏模式时,自动将三栏布局切换为 Tab 切换的单列布局。

优化触摸/鼠标交互体验(鼠标悬停显示商品详情,触摸滑动切换商品)

对于大屏设备,支持鼠标悬停显示商品详情可以提供更丰富的信息展示。对于小屏设备,触摸滑动切换商品可以提高操作便捷性。

以下是示例代码:

@Component
struct InteractiveProductCard {
    @Prop product: { id: number, name: string, image: Resource };
    @State isHover: boolean = false;

    build() {
        Column() {
            Image(this.product.image).width('100%').aspectRatio(1).objectFit(ImageFit.Contain).onHover((isHover) => {
                this.isHover = isHover;
            });
            Text(this.product.name).fontSize(16).textAlign(TextAlign.Center);
            if (this.isHover) {
                Text('商品详情信息...').fontSize(14).opacity(0.8);
            }
        }
          .padding(10)
          .backgroundColor('#FFFFFF')
          .borderRadius(10)
          .shadow({ color: '#00000020', offset: { x: 0, y: 2 }, blurRadius: 4 })
          .onTouch((event) => {
                if (event.type === TouchType.Swipe) {
                    // 触摸滑动切换商品逻辑
                }
            });
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

通过以上方案和代码示例,我们可以开发出一个跨端适配的电商购物应用,在不同屏幕大小的设备上提供优化的购物体验,并支持自由窗口模式。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签
收藏
回复
举报


回复
    相关推荐
    加入我们,一起打造面向云时代的开源操作系统!
    觉得TA不错?点个关注精彩不错过
    641
    帖子
    189
    视频
    3806
    声望
    38
    粉丝
    社区精华内容