HarmonyOS NEXT一行代码实现任意处弹窗
前言 从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?.() }) } } .w

前言
从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