前言
从Api9开始开发鸿蒙的大佬应该被自定义弹窗折腾得够呛,到目前为止我能想到的自定义弹窗方案有以下几种
promptAction.openCustomDialog(options: CustomDialogOptions)
(该方案@Builder装饰的视图(builder参数)必须定义在组件内部)CustomDialogController+CustomDialog
(该方案CustomDialogController仅在作为@CustomDialog和@Component struct的成员变量,
且在@Component struct内部定义时赋值才有效)windowStage.createSubWindow
(该方案需要对子窗口进行完美的控制,一旦逻辑控制不好容易出大问题)自定义一个@Component组件当做普通视图引入布局中(顶层位置),动态控制隐藏显示,达到弹窗的效果
使用Navigation时,将NavDestination的mode属性设置为NavDestinationMode.DIALOG
(优点:在使用Navigation做路由方案时,NavDestination弹窗方案不会覆盖即将要跳转的页面)(推荐)使用ComponentContent (本文要介绍的方法) (推荐)
除了3、5、6方案,其他方案都不方便在非组件中控制弹窗显示
效果图
一行代码任意处显示Loading和对话框或者自定义弹窗
//显示loading弹窗
Loading.show(UIContext)
//loading弹窗消失
Loading.dismiss()
//显示自定义弹窗
TestDialog.showDialog(UIContext)
实现在普通类中也可以控制弹窗显示
import { Dialog } from './Dialog'
import { Loading } from './Loading'
export class TestDialog {
static showLoading(ctx: UIContext) {
Loading.show(ctx)
}
static showDialog(ctx: UIContext) {
let param: DialogParams = {
title: "提示",
content: "是否确定要删除?",
leftText: "取消",
rightText: "确定",
leftClick: () => {
console.info("=========取消")
dialog.dismiss()
},
rightClick: () => {
console.info("=========确定")
dialog.dismiss()
}
} as DialogParams
let dialog = new BaseDialog(ctx)
dialog.setContentView(wrapBuilder(buildAlertDialogView), param)
dialog.show()
}
}
import { Loading } from './Loading';
import { TestDialog } from './TestDialog';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Column({ space: 10 }) {
Button("显示Loading")
.onClick(() => {
TestDialog.showLoading(this.getUIContext())
setTimeout(() => {
Loading.dismiss()
}, 2000)
})
Button("显示Dialog")
.onClick(() => {
TestDialog.showDialog(this.getUIContext())
})
}.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%')
}
}
封装后的使用方式
有参数
let param: DialogParams = {
title: "提示",
content: "是否确定要删除?",
leftText: "取消",
rightText: "确定",
leftClick: () => {
dialog.dismiss()
},
rightClick: () => {
dialog.dismiss()
}
} as DialogParams
let dialog = new BaseDialog(ctx)
dialog.setContentView(wrapBuilder(buildAlertDialogView), param)
dialog.show()
无参数
static showDialogNoData(ctx: UIContext) {
let dialog = new BaseDialog(ctx)
dialog.setContentView(wrapBuilder(buildAlertDialogViewNoData))
dialog.show()
}
有参数视图
```
export interface DialogParams {
title: string,
content: string,
leftText: string,
rightText: string,
leftClick?: () => void,
rightClick?: () => void,
}
```
@Builder
function buildAlertDialogView(param: DialogParams) {
Column() {
if (param.title) {
Text(param.title)
.width('100%')
.fontWeight(FontWeight.Medium)
.fontColor("#333333")
.fontSize(20)
.margin({ top: 17 })
}
Text(param.content)
.width('100%')
.fontWeight(FontWeight.Medium)
.fontColor("#666666")
.fontSize(14)
.margin({ top: 17, bottom: 8 })
Row() {
if (param.leftText) {
Button(param.leftText)
.height(40)
.fontSize(16)
.fontColor('#666666')
.layoutWeight(1)
.fontWeight(FontWeight.Medium)
.margin({ right: 5 })
.backgroundColor('#ffffff')
.onClick(() => {
param?.leftClick?.()
})
}
Button(param.rightText)
.height(40)
.fontSize(16)
.fontColor('#ff06bcef')
.layoutWeight(1)
.fontWeight(FontWeight.Medium)
.backgroundColor('#ffffff')
.onClick(() => {
param?.rightClick?.()
})
}
}
.width("90%")
.height('auto')
.padding({ left: 25, right: 25, bottom: 15 })
.backgroundColor(Color.White)
.borderRadius(25)
}
无参数视图
@Builder
function buildAlertDialogViewNoData() {
Column() {
Text('标题')
.width('100%')
.fontWeight(FontWeight.Medium)
.fontColor("#333333")
.fontSize(20)
.margin({ top: 17 })
Text('param.content')
.width('100%')
.fontWeight(FontWeight.Medium)
.fontColor("#666666")
.fontSize(14)
.margin({ top: 17, bottom: 8 })
Row() {
Button('取消')
.height(40)
.fontSize(16)
.fontColor('#666666')
.layoutWeight(1)
.fontWeight(FontWeight.Medium)
.margin({ right: 5 })
.backgroundColor('#ffffff')
.onClick(() => {
//param?.leftClick?.()
})
Button('确定')
.height(40)
.fontSize(16)
.fontColor('#ff06bcef')
.layoutWeight(1)
.fontWeight(FontWeight.Medium)
.backgroundColor('#ffffff')
.onClick(() => {
//param?.rightClick?.()
})
}
}
.width("90%")
.height('auto')
.padding({ left: 25, right: 25, bottom: 15 })
.backgroundColor(Color.White)
.borderRadius(25)
}
Dialog完整封装代码
import { ComponentContent, promptAction, PromptAction } from '@kit.ArkUI'
export class BaseDialog {
private _isShowing: boolean = false
private uiContext?: UIContext
private promptAction?: PromptAction
private contentNode?: ComponentContent<Object>
constructor(uiContext: UIContext) {
this.uiContext = uiContext
this.promptAction = this.uiContext.getPromptAction();
}
public setContentView<T extends Object>(builder: WrappedBuilder<[T]>|WrappedBuilder<[]>, args?: T) {
if (!this.uiContext) {
return
}
if (args) {
this.contentNode = new ComponentContent(this.uiContext, builder, args);
} else {
this.contentNode = new ComponentContent(this.uiContext, builder as WrappedBuilder<[]>);
}
}
public setData<T extends Object>(args: T) {
this.contentNode?.update(args)
}
public show(options?: promptAction.BaseDialogOptions) {
if (this._isShowing) {
return
}
this._isShowing = true
if (options) {
this.promptAction?.openCustomDialog(this.contentNode, options).then(() => {
}).catch(() => {
this._isShowing = false
});
} else {
this.promptAction?.openCustomDialog(this.contentNode).then(() => {
}).catch(() => {
this._isShowing = false
});
}
}
public dismiss() {
this._isShowing = false
if (this.contentNode) {
this.promptAction?.closeCustomDialog(this.contentNode).then(() => {
this.contentNode?.dispose()
this.uiContext = undefined
this.promptAction = undefined
this.contentNode = undefined
})
}
}
public isShowing(): boolean {
return this._isShowing
}
public update<T extends Object>(args: T) {
this.contentNode?.update(args)
}
public reuse<T extends Object>(args?: T) {
this.contentNode?.reuse(args)
}
public recycle() {
this.contentNode?.recycle()
}
public updateConfiguration() {
this.contentNode?.updateConfiguration()
}
}
Top comments (0)