简介
分布式购物车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. 新建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)
}
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()
}
}
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%')
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')
}
}
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')
}
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实例;
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
storeId | string | 是 | 数据库唯一标识符,长度不大于MAX_STORE_ID_LENGTH。 |
options | Options | 是 | 创建KVStore实例的配置信息。 |
3)KVStore数据库实例, KVStore.put提供增加数据的方法,如下是参数说明;
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
key | string | 是 | 要添加数据的key,不能为空且长度不大于MAX_KEY_LENGTH。 |
value | Uint8Array | string | number | boolean | 是 | 要添加数据的value,支持Uint8Array、number 、 string 、boolean,Uint8Array、string 的长度不大于MAX_VALUE_LENGTH。 |
callback | AsyncCallback | 是 | 回调函数。 |
4) KVStore数据库实例,KVStore.on订阅指定类型的数据变更通知;一般监听远端设备变化,再进行相应操作达到分布式数据共享的效果;
本d项目通过storeId 值不同,创建了两个数据库,分别是ShoppingCartsInfo类和TotalData类,ShoppingCartsInfo应用添加商品到购物车,TotalData应用在购物车列表进行勾选结算;如下是TotalData类流程
如下是ShoppingCartsInfo类流程
最后,有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(Harmony NEXT)资料用来跟着学习是非常有必要的。
为了能够帮助大家快速掌握鸿蒙(Harmony NEXT)应用开发技术知识。在此给大家分享一下我结合鸿蒙最新资料整理出来的鸿蒙南北向开发学习路线以及整理的最新版鸿蒙学习文档资料。
这份鸿蒙(Harmony NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点。
希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员,可以直接领取这份资料
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
鸿蒙(Harmony NEXT)最新学习路线
-
HarmonOS基础技能
- HarmonOS就业必备技能
- HarmonOS多媒体技术
- 鸿蒙NaPi组件进阶
- HarmonOS高级技能
- 初识HarmonOS内核
- 实战就业级设备开发
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
《鸿蒙 (OpenHarmony)开发入门教学视频》
《鸿蒙生态应用开发V2.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
- ArkTS语言
- 安装DevEco Studio
- 运用你的第一个ArkTS应用
- ArkUI声明式UI开发
- .……
《鸿蒙开发进阶》
- Stage模型入门
- 网络管理
- 数据管理
- 电话服务
- 分布式应用开发
- 通知与窗口管理
- 多媒体技术
- 安全技能
- 任务管理
- WebGL
- 国际化开发
- 应用测试
- DFX面向未来设计
- 鸿蒙系统移植和裁剪定制
- ……
《鸿蒙进阶实战》
- ArkTS实践
- UIAbility应用
- 网络案例
- ……
获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料
总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。