API12之前的弹窗实现方案
ArkUI提供了@CustomDialog
用于自定义弹窗,虽然可以完成业务要求的样式和功能,但是在实际项目开发中存在两个问题:
- 每次使用都需创建
CustomDialogController
实例并配置各项参数 CustomDialogController
只能在@CustomDialog
和@Component struct
中使用
在组件中每次使用都要创建控制器实例,增加多余代码。在方法或类中不能使用弹窗,在使用弹窗时视图和逻辑耦合过高。
解决方案
1、通过创建window模拟弹窗。
如果点击弹窗按钮进行路由跳转,新页面加载在弹窗window上。
2、使用Navigation.Dialog模拟弹窗。
项目路由跳转都是使用router控制,难以整体更换Navigation组件。
3、使用空组件配置CustomDialogController
和调用打开关闭弹窗方法,在类中在调用空组件的方法
@CustomDialog
struct DialogTest {
controller?: CustomDialogController
build() {
Text('弹窗展示内容')
}
}
// 这个空组件要在使用弹窗之前使用一下,保证temp.applyDialog(this)执行
@Component
export struct DialogComponent {
private controller = new CustomDialogController({
builder: DialogTest()
})
open() {
this.controller.open()
}
close() {
this.controller.close()
}
aboutToAppear(): void {
temp.applyDialog(this)
}
build() {
}
}
class DialogManage {
private d: DialogComponent | null = null
applyDialog(com: DialogComponent) {
this.d = com
}
open() {
this.d?.open()
}
close() {
this.d?.close()
}
}
const temp = new DialogManage()
export default temp
使用API12的openCustomDialog和ComponentContent实现
openCustomDialog
openCustomDialog
时UIcontext上API12新增的方法,传入ComponentContent
类型和弹窗相关配置即可使用弹窗。
UIcontext在struct中使用this.getUIContext()获取。在非struct中使用时,需要在onWindowStageCreate 钩子函数中通过AppStorage.setOrCreate('windowStage', windowStage)保存供全局使用,使用windowStage.getMainWindowSync().getUIContext()在任何位置都可获取UIcontext
ComponentContent
API12提供的模块,主要就是解决弹窗耦合问题的,底层使用的BuilderNode
,类似前端的虚拟节点,在js中创建合适的时机插入页面中。
通过BuilderNode提前创建加载的特性,可以做到耗时组件秒开的效果,例如web组件加载h5页面
简单使用
Button('弹窗').onClick(() => {
const context = this.getUIContext()
const node = new ComponentContent(context,wrapBuilder(testDialog), '测试弹窗')
context.getPromptAction().openCustomDialog(node)
})
// 弹窗主体
@Builder
function testDialog(params: string) {
Column() {
Text(params)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.margin({bottom: 36})
}
.width('80%')
.height('40%')
.backgroundColor('#FFF0F0F0')
}
在项目中使用openCustomDialog
创建弹窗其配置项基本相同,只有展示主体不同例如温馨提示弹窗、活动弹窗、引导弹窗等等。定义抽象类、基本弹窗配置并完成公共的相关配置和方法,项目中定义弹窗时继承此抽象类实现抽象方法即可快速创建弹窗。
基础配置类BaseDialogParam.ets
export class BaseDialogParam {
// 弹窗关闭回调
dismissAction: Function = () => {
}
// 弹窗将要显示的回调
showAction: Function = () => {
}
// 点击遮罩是否关闭弹窗 false: 不关闭
autoCancel: boolean = false
// 弹窗主体位置
alignment: DialogAlignment = DialogAlignment.Center
// 关闭打开时的动画效果
transition?: TransitionEffect
// 可以控制是否可以通过设备上的返回键、左、右滑、键盘的ESC关闭弹窗
onWillDismiss: (action: DismissDialogAction) => void = () => {
}
// 弹窗关闭时执行的方法
onDidDisappear?: () => void
}
抽象类BaseDialog.ets
// 继承此类的类属性是按照业务需求定制的弹窗,除基本参数外还有一些其它功能需要的参数
export abstract class BaseDialog<T extends BaseDialogParam> {
private promptAction?: PromptAction
private componentContent?: ComponentContent<T>
private context: UIContext
// 获取UIcontext
constructor(context?: UIContext) {
const windowStage = AppStorage.get<window.WindowStage>('windowStage')!
this.context = context ?? windowStage.getMainWindowSync().getUIContext()
}
show(param: T): Promise<void> {
this.promptAction = this.context.getPromptAction()
// 打开之前先关闭弹窗
param.dismissAction = () => {
this.dismiss()
}
this.componentContent = new ComponentContent(this.context, this.dialogBuilder(), param)
// 弹窗配置
return this.promptAction.openCustomDialog(this.componentContent, {
alignment: param.alignment,
autoCancel: param.autoCancel,
transition: param.transition,
onDidDisappear: param.onDidDisappear,
onWillDismiss: param.onWillDismiss,
onWillAppear: () => {
param.showAction()
}
}).catch(() => {
this.release()
})
}
// 更新传参
update(param: T) {
if (this.componentContent) {
this.componentContent.update(param)
}
}
// 弹窗主体
abstract dialogBuilder(): WrappedBuilder<T[]>
isShowing(): boolean {
if (this.componentContent) {
return true
}
return false
}
// 关闭弹窗
dismiss(): Promise<void> {
return new Promise((resolve) => {
if (this.componentContent) {
this.promptAction?.closeCustomDialog(this.componentContent).then(() => {
this.release()
resolve()
}).catch(() => {
this.release()
resolve()
})
} else {
resolve()
}
})
}
private release() {
this.componentContent?.dispose()
this.componentContent = undefined;
this.promptAction = undefined;
}
}
实现一个温馨提示弹窗TipsDialog
// 自定义弹窗中需要的参数和回调
class TipsDialogParam extends BaseDialogParam {
msg: string = ''
onsubmit: () => void = () => {
}
}
// 弹窗主体实现
@Builder
function TipsBuilder(param: TipsDialogParam) {
Column() {
Text('温馨提示').fontSize(26).fontWeight(700)
Text(param.msg).alignSelf(ItemAlign.Start)
Row({space: 15}) {
Button('关闭')
.layoutWeight(1)
.onClick(() => {
param.dismissAction()
})
Button('确认')
.layoutWeight(1)
.onClick(() => {
param.onsubmit()
})
}.width('100%')
}
.width('80%')
.height('30%')
.backgroundColor(Color.White)
.borderRadius(20)
.padding(25)
.justifyContent(FlexAlign.SpaceBetween)
}
export class TipsDialog extends BaseDialog<TipsDialogParam> {
// 打开弹窗
open(msg: string) {
const param = new TipsDialogParam()
param.msg = msg
param.onsubmit = () => {
promptAction.showToast({message: '点击了确认按钮'})
}
this.show(param)
}
// 实现BaseDialog中的抽象方法
dialogBuilder(): WrappedBuilder<TipsDialogParam[]> {
return wrapBuilder(TipsBuilder)
}
}
使用TipsDialog
Button('弹窗').onClick(() => {
const tipsDialog = new TipsDialog()
tipsDialog.open('提示内容')
})