【HarmonyOS Next】鸿蒙应用弹框和提示气泡详解(一)
【HarmonyOS Next】鸿蒙应用弹框和提示气泡详解(一)
一、前言
在应用开发过程中,弹框Dialog和提示气泡Toast的使用极为频繁,从事移动开发的同学对此应该相当熟悉。然而,在鸿蒙的响应式布局里,早期版本所提供的弹框Dialog和提示气泡Toast,使用起来存在诸多不便。这是因为它们与UI紧密绑定,在纯逻辑类文件中无法使用。
鉴于这一历史遗留问题,后续API迭代时,对鸿蒙的弹框Dialog和提示气泡Toast进行了升级。如今,它们已能在纯逻辑类中运用,实现了与UI的解耦。
迭代优化过程
从page界面UI上弹出 => 挂靠子窗口实现弹出 => UI框架层预留挂靠节点
通过上述迭代过程可以看出,目前与UI强绑定的实现方式API已被标注为不推荐使用。
二、鸿蒙中的弹框使用
综上所述,目前鸿蒙HarmonyOS对于弹框、提示气泡,以及相关延申组件(浮层,Popup,Menu, bindSheet, bindContentCover),其实现方案均是挂靠到UI框架的预留节点上,以此进行添加渲染。
1. 弹框的实现
目前弹框存在两种实现方式:
(1) 系统定制弹框,可以直接使用。
系统定制弹框依据业务复杂度的差异,又有两种封装方式:
分别是基础弹框(警告弹框,列表弹窗)
@Entry
@Component
struct AlertDialogTextPage {
build() {
Column() {
Button('showAlertDialog')
.margin(30)
.onClick(() => {
this.getUIContext().showAlertDialog(
{
title: 'title',
message: 'text',
autoCancel: true,
alignment: DialogAlignment.Center,
buttons: [{
value: 'cancel',
action: () => {
console.info('cancel')
}
},
{
enabled: true,
defaultFocus: true,
style: DialogButtonStyle.HIGHLIGHT,
value: 'ok',
action: () => {
console.info('ok')
}
}],
}
)
})
}
.width('100%')
.margin({ top: 5 })
}
}
还有带业务性质的PickerDialog弹框:
// 日历选择器弹窗示例 (CalendarPickerDialog)
@Entry
@Component
struct PickerDialogTextPage {
private selectedDate: Date = new Date('2024-04-23')
build() {
Column() {
Button("Show CalendarPicker Dialog")
.margin(20)
.onClick(() => {
console.info("CalendarDialog.show")
CalendarPickerDialog.show({
selected: this.selectedDate,
acceptButtonStyle: {
fontColor: '#2787d9',
fontSize: '16fp',
backgroundColor: '#f7f7f7',
borderRadius: 10
},
cancelButtonStyle: {
fontColor: Color.Red,
fontSize: '16fp',
backgroundColor: '#f7f7f7',
borderRadius: 10
},
onAccept: (date: Date)=>{
// 当弹出框再次弹出时显示选中的是上一次确定的日期
this.selectedDate = date
}
})
})
}.width('100%')
}
}
(2) 自定义弹框
需要创建节点对象,在节点对象中添加自定义弹框的布局:
@State message: string = "测试文本"
/**
* 自定义弹框布局
* @param params
*/
@Builder
function buildText(params: Params) {
Column() {
Text(params.text)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 36 })
Button('Close')
.onClick(() => {
PromptActionClass.closeDialog()
})
}.backgroundColor('#FFF0F0F0')
}
private contentNode: ComponentContent<Object> =
new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message));
之后获取PromptAction对象,调用openCustomDialog弹出自定义弹框:
this.getContext().getPromptAction().openCustomDialog(PromptActionClass.contentNode)
.then(() => {
console.info('OpenCustomDialog complete.')
})
.catch((error: BusinessError) => {
let message = (error as BusinessError).message;
let code = (error as BusinessError).code;
console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
})
当你需要修改自定义弹框的对齐方式,偏移量等,需要添加promptAction.BaseDialogOptions。
this.getContext().getPromptAction().openCustomDialog(PromptActionClass.contentNode,{ alignment: DialogAlignment.Top, offset: { dx: 0, dy: 50 } })
.then(() => {
console.info('OpenCustomDialog complete.')
})
2. 气泡的实现
气泡的实现包含对齐方式和悬浮态相关设置,但不能设置字体的大小和颜色。若要自定义字体大小和颜色,则需要使用自定义弹框或者Popup来实现。
import { promptAction } from '@kit.ArkUI'
import { BusinessError } from '@kit.BasicServicesKit'
/**
* 气泡示例
*/
@Entry
@Component
struct ToastTextPage {
build() {
Column() {
Button('Show toast').fontSize(20)
.onClick(() => {
try {
this.getUIContext().getPromptAction().showToast({
message: '测试气泡',
duration: 2000,
showMode: promptAction.ToastShowMode.TOP_MOST
});
} catch (error) {
let message = (error as BusinessError).message
let code = (error as BusinessError).code
console.error(`showToast args error code is ${code}, message is ${message}`);
};
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
三、源码示例
PromptActionClass.ets
import { BusinessError } from '@kit.BasicServicesKit';
import { ComponentContent, promptAction } from '@kit.ArkUI';
import { PromptAction, UIContext } from '@ohos.arkui.UIContext';
/**
* 自定义弹框封装
*/
export class PromptActionClass {
static ctx: UIContext;
static contentNode: ComponentContent<Object>;
static options: promptAction.BaseDialogOptions;
static setContext(context: UIContext) {
PromptActionClass.ctx = context;
}
static setContentNode(node: ComponentContent<Object>) {
PromptActionClass.contentNode = node;
}
static setOptions(options: promptAction.BaseDialogOptions) {
PromptActionClass.options = options;
}
static openDialog() {
if (PromptActionClass.contentNode!== null) {
PromptActionClass.ctx.getPromptAction().openCustomDialog(PromptActionClass.contentNode, PromptActionClass.options)
.then(() => {
console.info('OpenCustomDialog complete.')
})
.catch((error: BusinessError) => {
let message = (error as BusinessError).message;
let code = (error as BusinessError).code;
console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
})
}
}
static closeDialog() {
if (PromptActionClass.contentNode!== null) {
PromptActionClass.ctx.getPromptAction().closeCustomDialog(PromptActionClass.contentNode)
.then(() => {
console.info('CloseCustomDialog complete.')
})
.catch((error: BusinessError) => {
let message = (error as BusinessError).message;
let code = (error as BusinessError).code;
console.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
})
}
}
static updateDialog(options: promptAction.BaseDialogOptions) {
if (PromptActionClass.contentNode!== null) {
PromptActionClass.ctx.getPromptAction().updateCustomDialog(PromptActionClass.contentNode, options)
.then(() => {
console.info('UpdateCustomDialog complete.')
})
.catch((error: BusinessError) => {
let message = (error as BusinessError).message;
let code = (error as BusinessError).code;
console.error(`UpdateCustomDialog args error code is ${code}, message is ${message}`);
})
}
}
}
class Params {
text: string = ""
constructor(text: string) {
this.text = text;
}
}
/**
* 自定义弹框布局
* @param params
*/
@Builder
function buildText(params: Params) {
Column() {
Text(params.text)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 36 })
Button('Close')
.onClick(() => {
PromptActionClass.closeDialog()
})
}.backgroundColor('#FFF0F0F0')
}
/**
* 首页
*/
@Entry
@Component
struct Index {
@State message: string = "hello"
private ctx: UIContext = this.getUIContext();
private contentNode: ComponentContent<Object> =
new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message));
@State handlePopup: boolean = false
aboutToAppear(): void {
PromptActionClass.setContext(this.ctx);
PromptActionClass.setContentNode(this.contentNode);
PromptActionClass.setOptions({ alignment: DialogAlignment.Top, offset: { dx: 0, dy: 50 } });
}
build() {
Row() {
Column() {
/**
* 显示自定义气泡 更新样式
*/
Button("open dialog and update options")
.margin({ top: 50 })
.onClick(() => {
PromptActionClass.openDialog()
setTimeout(() => {
PromptActionClass.updateDialog({
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -50 }
})
}, 1500)
})
/**
* 显示自定义气泡 更新内容数据
*/
Button("open dialog and update content")
.margin({ top: 50 })
.onClick(() => {
PromptActionClass.openDialog()
setTimeout(() => {
this.contentNode.update(new Params('update'))
}, 1500)
})
/**
* 气泡菜单
*/
Button('PopupOptions')
.onClick(() => {
this.handlePopup =!this.handlePopup
})
.bindPopup(this.handlePopup, {
message: 'This is a popup with PopupOptions',
})
/**
* 提示气泡
*/
Button('show Toast')
.onClick(() => {
let promptAction: PromptAction = this.ctx.getPromptAction();
try {
promptAction.showToast({
message: 'Message Info',
duration: 2000
});
} catch (error) {
let message = (error as BusinessError).message;
let code = (error as BusinessError).code;
console.error(`showToast args error code is ${code}, message is ${message}`);
};
})
/**
* 系统弹框
*/
Button('show Toast')
.onClick(() => {
let promptAction: PromptAction = this.ctx.getPromptAction();
try {
promptAction.showToast({
message: 'Message Info',
duration: 2000
});
} catch (error) {
let message = (error as BusinessError).message;
let code = (error as BusinessError).code;
console.error(`showToast args error code is ${code}, message is ${message}`);
};
})
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
}
}
- 点赞
- 收藏
- 关注作者
评论(0)