📝往期推文全新看点(文中附带最新·鸿蒙全栈学习笔记)
🚩 鸿蒙(HarmonyOS)北向开发知识点记录~
🚩 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
🚩 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
🚩 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
🚩 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
🚩 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
🚩 记录一场鸿蒙开发岗位面试经历~
📃 持续更新中……
概述
本文从目前流行的垂类市场中,选择移动支付应用作为典型案例详细介绍 “一多” 在实际开发中的应用,同时提供该垂类应用在多设备上的界面设计与实现参考。本文选择移动支付首页作为典型页面进行开发,涉及的核心功能包括支付、收款、扫码等。
本文重点内容如下:
- 扫一扫功能实现。
- 收付款功能实现。
下面的章节将分别从 UX设计、 架构设计、页面开发三个角度给出推荐的参考样例,介绍“一多”移动支付应用在开发过程中的最佳实践。
架构设计
HarmonyOS应用的分层架构主要包括三个层次:产品定制层、基础特性层和公共能力层,为开发者构建了一个清晰、高效、可扩展的设计架构。
页面开发
本章介绍移动支付应用中如何使用“一多”的布局能力,完成页面层级的一套页面、多端适配。下文将从不同页面展开,介绍每个页面区域使用到具体的布局能力,帮助开发者从零到一进行移动支付应用的开发。
首页
移动应用首页主要涉及扫一扫、支付、收付款等能力。观察首页在多设备上的UX设计图,可以进行如下设计:
- 将首页划分为6个部分,效果图如下:
示意图 | sm | mg | lg |
---|---|---|---|
效果图 | ![]() | ![]() | ![]() |
区域编号 | 简介 | 实现方案 |
---|---|---|
1 | 底部/侧边页签 | 借助 栅格布局 监听断点变化改变位置。 |
2 | 城市及搜索框 | 使用 拉伸能力 结合断点控制元素尺寸,同时采用 Blank组件 填充中间空白区域。 |
3 | 金刚区 | 使用 栅格布局 结合断点变化改变快捷功能的形态。 |
4 | 功能入口合集 | 采用 重复布局,在更大尺寸设备上显示更多的功能入口。 |
5 | 服务卡片 | 采用 重复布局,手机上的单列信息,在折叠屏和2in1上重复布局。 |
6 | 财富精选 | 采用 栅格布局 ,在更大尺寸设备上显示更多列数。 |
- 金刚区
使用栅格布局配合断点控制对应的快捷功能的显示形态,在sm断点下按钮显示为圆形图标且上图标下文字的形式,在mg、lg断点下显示为圆角矩形且左图标右文字形式。
示意图 | sm | md | lg |
---|---|---|---|
效果图 | ![]() | ![]() | ![]() |
// src/main/ets/pages/Home.ets
GridRow({
gutter: {
x: {
sm: 30,
md: 41,
lg: 58
}
}
}) {
ForEach(this.quickFunctions, (item: QuickFunctionItem) => {
GridCol({ span: 3 }) {
if (this.curBp === 'sm') {
QuickFunctionCardCircle({ quickFunctionItem: item })
} else {
QuickFunctionCardSquare({ quickFunctionItem: item })
}
}
// ...
}, (item: QuickFunctionItem) => item.getText())
}
- 功能入口合集
使用重复布局,配合栅格布局控制不同设备上设备的显示列数。在sm断点下设置GridRow的columns属性为4,在md断点下设置GridRow的columns属性为6,在lg断点下设置GridRow的columns属性为8。同时通过GridRow的onBreakpointChange回调控制渲染数据源的条数,在sm、md、lg断点下分别渲染8、12、16条数据。
示意图 | sm | md | lg |
---|---|---|---|
设计能力点 | ![]() | ||
效果图 | ![]() | ![]() | ![]() |
// src/main/ets/pages/Home.ets
GridRow({
columns: {
sm: 4, // sm断点下显示4列
md: 6, // md断点下显示6列
lg: 8 // lg断点下显示8列
},
gutter: {
x: {
sm: 45,
md: 50,
lg: 55
},
y: {
sm: 16,
md: 24,
lg: 28
}
}
}) {
ForEach(this.functions, (item: QuickFunctionItem) => {
GridCol() {
FunctionCard({ data: item })
}
}, (item: QuickFunctionItem, index: number) => index + JSON.stringify(item))
}
.onBreakpointChange((breakpoints: string) => {
// 根据断点情况返回不同的渲染数据条数
this.functions = new PayHubViewModel().getFunctionsByBreakpoints(breakpoints);
// ...
})
-
服务卡片
使用重复布局配合栅格布局控制不同设备尺寸下的显示列数。在sm断点下设置GridCol的span属性为12,在md断点下设置GridCol的span属性为6,在lg断点下设置GridCol的span属性为3。同时配合GridRow的onBreakpointChange回调控制渲染的数据条数,使得在sm、md、lg断点下分别渲染1、2、4条数据。
示意图 | sm | md | lg |
---|---|---|---|
设计能力点 | ![]() | ||
效果图 | ![]() | ![]() | ![]() |
// src/main/ets/pages/Home.ets
GridRow({
gutter: { x: { sm: 15 } }
}) {
ForEach(this.sampleImages, (item: Resource) => {
GridCol({
span: {
sm: 12, // 在sm断点下设置span为12
md: 6, // 在md断点下设置span为6
lg: 3 // 在lg断点下设置span为3
}
}) {
Image(item)
.width('100%')
}
}, (item: Resource, index: number) => index + JSON.stringify(item))
}
.onBreakpointChange((breakpoints: string) => {
// 根据断点情况返回不同的渲染数据条数
this.sampleImages = new ServiceCardViewModel().getImagesByBreakpoints(breakpoints);
})
-
财富精选
使用栅格布局控制不同设备尺寸下显示列数,在sm断点下设置GridCol占用span为12,在md断点下设置GridCol的span属性为6,在lg断点下设置GridCol的span属性为3。
示意图 | sm | md | lg |
---|---|---|---|
效果图 | ![]() | ![]() | ![]() |
// src/main/ets/pages/Home.ets
GridRow({
gutter: { x: { sm: 15 }, y: { sm: 15 } }
}) {
ForEach(this.fortunePicks, (item: Resource) => {
GridCol({
span: {
sm: 12,
md: 6,
lg: 3
}
}) {
Image(item)
.width('100%')
}
}, (item: Resource, index: number) => index + JSON.stringify(item))
}
- 收付款功能
收付款功能在手机设备上以单独的界面进行呈现,而在折叠屏及更大尺寸设备上以弹窗的方式进行呈现。UX设计图如下:
示意图 | sm | md | lg |
---|---|---|---|
效果图 | ![]() | ![]() | ![]() |
实现上需要根据当前设备的断点尺寸,来决定是使用路由跳转到收付款页,还是以弹窗的方式弹出收付款对话框,代码参考如下:
// src/main/ets/pages/Home.ets
private receivePayment = () => {
if (this.curBp === 'sm') {
this.pathInfos.pushPath({ name: 'ReceivePaymentPage' });
} else if (!this.isDialogOpen) {
this.isDialogOpen = true;
}
};
自定义收款码生成需要使用到Scan Kit中的 码图生成能力 ,关键为使用 createBarcode 接口依照收款所需的关键信息生成对应的二维码。参考代码如下:
// src/main/ets/pages/Home.ets
// 由getQRCode函数返回根据关键信息生成的二维码
let content: string = this.getQRCode();
// 配置二维码参数
const options: generateBarcode.CreateOptions = {
scanType: scanCore.ScanType.QR_CODE,
height: Constants.QRCODE_SIZE,
width: Constants.QRCODE_SIZE
};
// 调用码图生成接口
generateBarcode.createBarcode(content, options, (error: BusinessError, pixelMap: image.PixelMap) => {
if (error) {
Logger.error(`Callback error: ${JSON.stringify(error)}`);
return;
}
// 设置PixelMap格式图片,使二维码加载显示到页面上
this.pixelMap = pixelMap;
});
收付款界面开发时常见的问题如下:
二维码定时动态刷新。可以在组件展示或弹窗开启时,启动一个定时器并在回调中传入一个更新二维码的函数,定时地向服务端发起请求获取最新的二维码并对本地二维码进行更新,在组件销毁或弹窗关闭时需要销毁定时器。组件情况下解决二维码定时动态刷新可参考如下代码:
// src/main/ets/pages/ReceivePaymentPage.ets
private timer: number | null = null;
// 在getAndUpdateQRCode函数中向服务端请求最新的二维码,并对本地二维码进行更新
private getAndUpdateQRCode(): void {
// ...
}
aboutToAppear(): void {
this.getAndUpdateQRCode();
this.timer = setInterval(() => {
this.getAndUpdateQRCode();
}, 60 * 1000); // 每60秒更新一次二维码
}
aboutToDisappear(): void {
clearInterval(this.timer);
this.timer = null;
}
弹窗情况下解决二维码定时动态刷新可参考如下代码:
// src/main/ets/pages/Home.ets
private timer: number | null = null;
// 在getAndUpdateQRCode函数中向服务端请求最新的二维码,并对本地二维码进行更新
private getAndUpdateQRCode(): void {
// ...
}
aboutToAppear(): void {
this.getAndUpdateQRCode();
this.timer = setInterval(() => {
this.getAndUpdateQRCode();
}, 60 * 1000);
}
aboutToDisappear(): void {
this.pixelMap = null;
clearInterval(this.timer);
this.timer = null;
}
折叠屏在折叠态与展开态变化时,页面切换流畅。如果应用处在收付款界面,折叠屏从折叠态到展开态,如果不做特殊操作,应用将仍然处在收付款的界面,只是整体尺寸被拉大,同样的,在从展开态到折叠态如果已有弹窗,也会导致整个界面错乱。解决方式为在断点变化时,监听当前路由状态并且控制弹出框的显示与关闭。
// src/main/ets/pages/Home.ets
@StorageProp('currentBreakpoint') @Watch('onBreakpointChange') curBp: string = Constants.BREAKPOINT_SM;
@State @Watch('onDialogStatusChange') isDialogOpen: boolean = false;
// ...
onDialogStatusChange() {
if (this.isDialogOpen) {
this.dialogController.open();
} else {
this.dialogController.close();
}
}
onBreakpointChange() {
const allPath = this.pathInfos.getAllPathName();
const currentName: string = allPath[this.pathInfos.size() - 1];
if (this.curBp !== 'sm' && currentName === 'ReceivePaymentPage') {
this.pathInfos.clear();
setTimeout(() => {
this.isDialogOpen = true;
}, 1000);
}
if (this.isDialogOpen && this.curBp === 'sm') {
this.isDialogOpen = false;
this.pathInfos.pushPath({ name: 'ReceivePaymentPage' });
}
// ...
}
应用窗口尺寸动态变化的时候防止二维码变形。解决方式为可以设置二维码的大小为固定百分比或者使用 aspectRatio 固定宽高比保证图片不变形。
点击 createBarcode 获取更多该接口使用指导。
- 扫一扫
示意图 | sm | md | lg |
---|---|---|---|
效果图 | ![]() | ![]() | ![]() |
扫一扫功能主要使用Scan Kit中的 自定义界面扫码能力 来自定义扫码的界面设计。
- 第三方支付链接
示意图 | sm | md | lg |
---|---|---|---|
效果图 | ![]() | ![]() | ![]() |
第三方支付页面使用断点控制不同的付款展示方式,在sm断点下以单独付款界面展示,在md、lg断点下以弹窗的方式展示。
- 卡包
示意图 | sm | md | lg |
---|---|---|---|
效果图 | ![]() | ![]() | ![]() |
使用 栅格布局 ,控制卡片在不同设备上的span占用,在sm断点下span占用为12,在md断点下span占用为6,在lg断点下span占用为4,使其分别在sm、md、lg断点下显示1、2、3列。
- 银行卡
示意图 | sm | md | lg |
---|---|---|---|
效果图 | ![]() | ![]() | ![]() |
区域编号 | 简介 | 实现方案 |
---|---|---|
1 | 顶部栏 | 顶部栏空白区域使用Blank组件实现拉伸能力。 |
2 | 卡片集合 | 使用栅格布局,在sm、md、lg设备尺寸下分别显示1、2、3列。 |
3 | 功能入口 | 使用栅格布局实现 重复布局 ,在sm设备上显示4列,在md设备上显示7列,在lg设备上显示9列。 |
4 | 银行卡优惠 | 使用栅格布局实现 重复布局 ,在sm设备上显示1列,在md设备上显示2列,在lg设备上显示3列。 |
- 添加银行卡
示意图 | sm | md | lg |
---|---|---|---|
效果图 | ![]() | ![]() | ![]() |
添加银行卡功能可以使用 半模态转场 ,默认情况下在sm设备下可以自动从底部进行弹窗,在sm断点以上不包括sm断点以居中弹窗方式显示内容。