当前位置:   article > 正文

OpenHarmony分布式购物车案例展示~_openharmony 分布式 应用场景

openharmony 分布式 应用场景

简介

分布式购物车demo 模拟的是我们购物时参加满减活动,进行拼单的场景;实现两人拼单时,其他一人添加商品到购物车,另外一人购物车列表能同步更新,且在购物车列表页面结算时,某一人结算对方也能实时知道结算金额和优惠金额。整个操作效果分为3个小动画,

  • 拉起对方用户

  • 添加商品到购物车列表

  • 购物车列表勾选

  • demo效果(HH-SCDAYU200)

工程目录

完整的项目结构目录如下

├─entry\\src\\main
│          │  config.json  应用配置文件
│          │ 
│          ├─ets
│          │  └─MainAbility
│          │      │  app.ets  ets应用程序主入口
│          │      │ 
│          │      ├─model
│          │      │      ArsData.ets     // 初始化我的页面数据
│          │      │      CommonLog.ets   // 日志类
│          │      │      GoodsData.ets   // 初始化商品信息数据类
│          │      │      MenuData.ets    // 初始化我的页面数据类
│          │      │      RemoteDeviceManager.ets  // 分布式拉起设备管理类
│          │      │      ShoppingCartDistributedData.ets  // 加入购物车分布式数据库
│          │      │      TotalSelectedDistributedData.ets // 结算购物车分布式数据库
│          │      │ 
│          │      └─pages
│          │              DetailPage.ets   // 商品详情页面
│          │              HomePage.ets     // 应用首页
│          │              MyPage.ets       // 我的页面
│          │              ShoppingCartListPage.ets  // 购物车列表页面
│     └─resources // 静态资源目录
│         ├─base
│         │  ├─element
│         │  ├─graphic
│         │  ├─layout
│         │  ├─media // 存放媒体资源
│         │  └─profile
│         └─rawfile
  • 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

开发步骤

1. 新建OpenHarmony ETS项目

在DevEco Studio中点击File -> New Project ->[Standard]Empty Ability->Next,Language 选择ETS语言,最后点击Finish即创建成功。

2. 编写商品展示主页面

效果图如上可以分为两部分

2.1商品列表展示

1)首先在@entry组件入口build()中使用 Tabs作为容器,达到排行榜和推荐翻页的效果;

2)再通过 List 包裹 Row 布局依次写入 Column 包裹的三个 Text 组件和 Image 组件;

3)并通过 Navigator 组件实现点击商品跳转到商品详细页功能,页面跳转过程使用 pageTransition 转场动画

Tabs() {
       TabContent() {
         GoodsList({ goodsItems: this.goodsItems});
       }
       .tabBar("畅销榜")
       .backgroundColor(Color.White)

       TabContent() {
         GoodsList({ goodsItems: this.goodsItems});
       }
       .tabBar("推荐")
       .backgroundColor(Color.White)
     }
      Navigator({ target: 'pages/DetailPage' }) {
       Row({ space: '40lpx' }) {
         Column() {
           Text(this.goodsItem.title)
             .fontSize('28lpx')
           Text(this.goodsItem.content)
             .fontSize('20lpx')
           Text('¥' + this.goodsItem.price)
             .fontSize('28lpx')
             .fontColor(Color.Red)
         }
         .height('160lpx')
         .width('50%')
         .margin({ left: '20lpx' })
         .alignItems(HorizontalAlign.Start)

         Image(this.goodsItem.imgSrc)
           .objectFit(ImageFit.ScaleDown)
           .height('160lpx')
           .width('40%')
           .renderMode(ImageRenderMode.Original)
           .margin({ right: '20lpx', left: '20lpx' })

       }
       .height('180lpx')
       .alignItems(VerticalAlign.Center)
       .backgroundColor(Color.White)
     }
     .params({ goodsItem: this.goodsItem ,ShoppingCartsGoods:this.ShoppingCartsGoods})
     .margin({ left: '40lpx' })
   }
   // 转场动画使用系统提供的多种默认效果(平移、缩放、透明度等)
 pageTransition() {
   PageTransitionEnter({ duration: 1000 })
     .slide(SlideEffect.Left)
   PageTransitionExit({ duration: 1000  })
     .slide(SlideEffect.Right)
 }
  • 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
2.2底部导航栏

1)通过 Row 包裹三个 Image 组件,并添加onClick 点击事件,修改 @Consume 修饰的变量,从而改变 @Provide 装饰的变量,再通过条件渲染展示不同的页面内容;

Flex() {
        Image(this.iconPath[0])
          .objectFit(ImageFit.Cover)
          .height('60lpx')
          .width('60lpx')
          .margin({left:'50lpx',right:'40lpx'})
          .onClick(() => {
            this.iconPath[0] = this.iconPathSelectsTmp[0]
            this.iconPath[1] = this.iconPathTmp[1]
            this.iconPath[2] = this.iconPathTmp[2]
            this.currentPage = 1
          })
        Image(this.iconPath[1])
          .objectFit(ImageFit.Cover)
          .height('60lpx')
          .width('60lpx')
          .margin({left:'40lpx',right:'40lpx'})
          .onClick(() => {
            this.iconPath[0] = this.iconPathTmp[0]
            this.iconPath[1] = this.iconPathSelectsTmp[1]
            this.iconPath[2] = this.iconPathTmp[2]
            this.currentPage = 2
            this.remoteData.putData("shopping_cart", this.ShoppingCartsGoods)
          })
        Image(this.iconPath[2])
          .objectFit(ImageFit.Cover)
          .height('60lpx')
          .width('60lpx')
          .margin({left:'40lpx',right:'50lpx'})
          .onClick(() => {
            this.iconPath[0] = this.iconPathTmp[0]
            this.iconPath[1] = this.iconPathTmp[1]
            this.iconPath[2] = this.iconPathSelectsTmp[2]
            this.currentPage = 3
          })
      }
    .margin({top:'20lpx'})
    }
      
      Column() {
          if (this.currentPage == 1) {
            Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.End }) {
              Image($r("app.media.icon_share"))
                .objectFit(ImageFit.Cover)
                .height('60lpx')
                .width('60lpx')
            }
            .width("100%")
            .margin({ top: '20lpx', right: '50lpx' })
            .onClick(() => {
              this.playerDialog.open()
            })

            GoodsHome({ goodsItems: this.goodsItems})
          }
          else if (this.currentPage == 3) {
            //我的
            MyInfo()
          }
        }
  • 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
3. 编写商品详细页面
3.1顶部滑动组件

1)滑动容器,提供切换子组件显示的能力;

Swiper() {
        ForEach(this.detailImages, item => {
          Image(item)
            .height('400lpx')
            .width('100%')
        })
      }
      .index(0)
      .autoPlay(true)
      .interval(3000)
      .indicator(true)
      .loop(true)
      .height('440lpx')
      .width('100%')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
3.2 自定义弹框

1)通过 @CustomDialog装饰器来创建自定义弹窗,使用方式可参考 自定义弹窗

2)规则弹窗效果如下,弹窗组成由两个 Text 和两个 Button 竖向排列组成;

所有我们可以在build()下使用 Flex 容器来包裹,组件代码如下:

@CustomDialog
struct CustomDialogExample {
  controller: CustomDialogController
  cancel: () => void
  confirm: () => void
  ShoppingCartsGoods: any[]

  build() {
    Flex() {
      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
        Text('加入购物车成功')
          .fontColor("#000000")
          .fontSize('40lpx')
          .margin({ top: '20lpx', bottom: "20lpx" })

        Flex({ justifyContent: FlexAlign.SpaceAround }) {
          Button('取消')
            .onClick(() => {
              this.controller.close()
              this.cancel()
            }).backgroundColor(0xffffff).fontColor(Color.Black)
          Button('确定')
            .onClick(() => {
              this.controller.close()
              this.confirm()
            }).backgroundColor(0xffffff).fontColor(Color.Red)
        }.margin({ bottom: "20lpx" })
      }
    }
    .height('200lpx')
  }
}
  • 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

​3)在@entry创建CustomDialogController对象并传入弹窗所需参数,后面可通过该对象open()和close()方法进行打开和关闭弹窗;

dialogController: CustomDialogController = new CustomDialogController({
    builder: CustomDialogExample({
      cancel: this.onCancel,
      confirm: this.onAccept,
      ShoppingCartsGoods: this.ShoppingCartsGoods
    }),
    cancel: this.existApp,
    autoCancel: true
  })
  onCancel() {
    CommonLog.info('Callback when the first button is clicked')
  }

  onAccept() {
    CommonLog.info('Callback when the second button is clicked')
    router.push({
      uri: "pages/HomePage",
      params: { dataList: this.ShoppingCartsGoods }
    })
  }

  existApp() {
    CommonLog.info('Click the callback in the blank area')
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
4. 添加分布式流转

分布式流转需要在同一网络下通过 DeviceManager组件 进行设备间发现和认证,获取到可信设备的deviceId调用 featureAbility.startAbility ,即可把应用程序流转到另一设备。

1)创建DeviceManager实例;

2)调用实例的startDeviceDiscovery(),开始设备发现未信任设备;

3)设置设备状态监听on(‘deviceFound’,callback),获取到未信任设备,并用discoverList变量进行维护;

4)传入未信任设备参数,调用实例authenticateDevice方法,对设备进行PIN码认证;

5)若是已信任设备,可通过实例的getTrustedDeviceListSync()方法来获取设备信息;

6)将设备信息中的deviceId传入 `featureAbility.startAbility方法,实现流转;

7)流转接收方可通过 featureAbility.getWant()获取到发送方携带的数据;

项目中将上面设备管理封装至RemoteDeviceManager,通过RemoteDeviceManager的四个方法来动态维护deviceList设备信息列表,实现分布式流转只需要在deviceList中获取deviceId,然后调用featureAbility.startAbility并携带数据,即可实现分布式流转。

5.分布式数据管理

分布式数据管理 要求两个或多个设备在同一网络,才能监听到数据库的改变,从而渲染页面;开发步骤:

1)创建一个KVManager对象实例,用于管理数据库对象;

2)通过指定Options和storeId,创建并获取KVStore数据库,如下是参数说明;需要先通过createKVManager构建一个KVManager实例;

参数名类型必填说明
storeIdstring数据库唯一标识符,长度不大于 MAX_STORE_ID_LENGTH。
optionsOptions创建KVStore实例的配置信息。

3)KVStore数据库实例, KVStore.put提供增加数据的方法,如下是参数说明;

参数名类型必填说明
keystring要添加数据的key,不能为空且长度不大于 MAX_KEY_LENGTH 。
valueUint8Arraystringnumber
callbackAsyncCallback回调函数。

4) KVStore数据库实例,KVStore.on订阅指定类型的数据变更通知;一般监听远端设备变化,再进行相应操作达到分布式数据共享的效果;

本d项目通过storeId 值不同,创建了两个数据库,分别是ShoppingCartsInfo类和TotalData类,ShoppingCartsInfo应用添加商品到购物车,TotalData应用在购物车列表进行勾选结算;如下是TotalData类流程

如下是ShoppingCartsInfo类流程

项目下载和导入

1)git下载

git clone https://gitee.com/openharmony-sig/knowledge_demo_shopping.git  --depth=1
  • 1

2)项目导入

打开DevEco Studio,点击File->Open->下载路径/FA/Shopping/DistributedShoppingCart

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:https://qr21.cn/FV7h05

入门必看:https://qr21.cn/FV7h05
1.  应用开发导读(ArkTS)
2.  ……

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门:https://qr21.cn/FV7h05
1.  基本概念
2.  构建第一个ArkTS应用
3.  构建第一个JS应用
4.  ……

开发基础知识:https://qr21.cn/FV7h05
1.  应用基础知识
2.  配置文件
3.  应用数据管理
4.  应用安全管理
5.  应用隐私保护
6.  三方应用调用管控机制
7.  资源分类与访问
8.  学习ArkTS语言
9.  ……

基于ArkTS 开发:https://qr21.cn/FV7h05
1.  Ability开发
2.  UI开发
3.  公共事件与通知
4.  窗口管理
5.  媒体
6.  安全
7.  网络与链接
8.  电话服务
9.  数据管理
10.  后台任务(Background Task)管理
11.  设备管理
12.  设备使用信息统计
13.  DFX
14.  国际化开发
15.  折叠屏系列
16.  ……

鸿蒙开发面试真题(含参考答案):https://qr21.cn/FV7h05

结语

不久前,华为在致敬鸿蒙开发者公开信中表示:

“HarmonyOS 的辉煌属于每一位辛勤耕耘的开发者,你们是 HarmonyOS 走向成功的奠基者,更是信息技术产业繁荣发展的先行者。”

事实上,鸿蒙系统生态能够在问世至今不到五年的时间里迅速壮大,并且在今年勇敢地迈向“纯原生”的新阶段,华为对开发人才的重视,可列为最重要的因素之一。如今就业市场上鸿蒙人才被“爆抢”,何尝不是行业规律对华为重视人才的一种“回报”?

而让每一位开发人才都能够发光发亮,才能走出全新的生态之路,从而更进一步点亮我国信息技术产业发展的前路。#春招鸿蒙岗位需求是去年近3倍# #鸿蒙程序员平均月薪超1万8

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/181985
推荐阅读
  

闽ICP备14008679号