自定义弹窗选型
合理选择不同的系统能力实现弹窗,有利于提升应用开发效率,实现更好的功能需求,因此了解自定义弹窗的选型和差异非常重要。在应用开发中,为了选择出合适的弹窗选型,从使用场景上,需要重点关注以下两点:
-
弹窗与界面代码解耦
在开发业务逻辑时,例如遇到一些网络请求失败的场景,需要触发相应的弹窗提醒用户进行操作,由于在任何页面都有可能触发对应的弹窗,此时弹窗不是与某个页面相关联,这个情况下,就需要弹窗与界面的解耦。
-
弹窗在界面跳转后保留
在一些权限配置页,用户首次进入应用时会弹出权限配置弹窗,让用户进行操作,此时若点击跳转到隐私详情页面,返回后弹窗需要保留在页面上。
从能力角度,系统提供了四种不同的方式来实现自定义弹窗,分别是CustomDialog、promptAction、UIContext.getPromptAction、Navigation.Dialog,在开发业务时,需要结合每种弹窗的特点来选择弹窗。
-
CustomDialog弹窗,必须在@Component struct内部定义,即在UI层创建控制器,当一个页面需要弹出多个自定义弹窗时,就需要创建对应个数的CustomDialogController,这会造成UI层代码冗余,无法做到弹窗与界面的解耦。
-
promptAction弹窗,为了解决CustomDialog弹窗的问题,支持了UI元素复用机制@Builder,但依赖UI组件。
-
UIContext.getPromptAction弹窗,基于promptAction弹窗演进而来,支持全局自定义弹窗,不依赖UI组件,依赖UIContext,支持在非页面文件中使用,弹窗内容支持动态修改,支持自定义弹窗圆角半径、大小和位置,适合在与页面解耦的全局弹窗、自定义弹窗显示和退出动画等场景下使用。
-
Navigation.Dialog弹窗,基于Navigation路由形式,以Component组件页面存在于路由栈中,以进出栈的方式打开或关闭弹窗,可以实现弹窗与UI界面解耦,默认透明显示,适合在切换页面弹窗不消失场景下使用。
上述四种弹窗的使用区别,可以从是否支持弹窗与界面代码解耦、弹窗在界面跳转后是否保留这两个方面去进行对比,总结如下:
CustomDialog | promptAction | UIContext.getPromptAction | Navigation.Dialog | |
---|---|---|---|---|
弹窗与界面代码解耦 | 不支持 | 不支持 | 支持 | 支持 |
弹窗在界面跳转后保留 | 否 | 否 | 否 | 是 |
在对解耦要求不高的情况下,可以使用上面CustomDialog、promptAction两个弹窗,本文围绕开发时主要常见的场景和问题,介绍UIContext.getPromptAction、Navigation.Dialog两个弹窗的使用。
用鸿蒙showCustomMsg提示框(标题和内容无法设置样式居中,总结就是一个字:丑!)
static showCustomMsg(title: string, content: string | Resource, confirmBtn?: string, cancelBtn?: string,
confirm?: () => void, cancel?: () => void): void {
promptAction.showDialog({
title: title ?? "提示",
message: content,
buttons: [
{
text: cancelBtn ?? "取消",
color: "#000000"
},
{
text: confirmBtn ?? "确认",
color: "#4169E1"
}
]
}).then(data => {
if (data.index == 1) {
if (confirm) {
confirm()
}
} else {
if (cancel) {
cancel()
}
}
})
}
直接使用鸿蒙showCustomMsg弹框在一些场景是可以的,但是局限性比较大,在一些别的场景需要自定义样式,或者就只是简单想让标题和内容居中都是没有提供样式来调整,因此我们可以使用以下弹框来自定义。
一、在原生页面使用自定义弹窗
import Logger from '../common/utils/Logger'
@CustomDialog
export struct HMCustomDialog {
controller: CustomDialogController
title: string | Resource = ''
message: string | Resource = ''
leftText: string | Resource = ''
rightText: string | Resource = ''
showLeft: boolean = true
showRight: boolean = true
cancel: () => void = () => {
}
confirm: () => void = () => {
}
build() {
Column() {
Text(this.title)
.fontSize(18)
.fontColor(Color.Black)
.fontWeight(800)
.margin({ top: 10 })
Text(this.message)
.fontSize(16)
.fontColor($r('app.color.font_333333'))
.margin({ top: 10, bottom: 10 })
Flex({ justifyContent: FlexAlign.SpaceAround }) {
Button(this.leftText)
.onClick(() => {
this.cancel()
this.controller.close()
}).backgroundColor(0xffffff)
.fontColor(Color.Gray)
.visibility(this.showLeft ? Visibility.Visible : Visibility.None)
Button(this.rightText)
.onClick(() => {
this.confirm()
Logger.debug('confirm')
this.controller.close()
}).backgroundColor(0xffffff)
.fontColor($r('app.color.font_0086f3'))
.visibility(this.showRight ? Visibility.Visible : Visibility.None)
}.margin({ bottom: 4 })
}
}
}
在父页面使用
MyDialogController: CustomDialogController = new CustomDialogController({
builder: HMCustomDialog({
title: '提示',
message: '您还没有登录',
leftText: '取消',
rightText: '去登录',
showLeft: true,
showRight: true,
cancel: () => {
},
confirm: () => {
//去登录
WindowUtils.getRouter().pushUrl({ url: 'pages/login/LoginPage' })
},
}),
alignment: DialogAlignment.Center
})
build() {
Column(){
Button()
.onClick(()=>{
this.MyDialogController.open()
})
}
}
二、在非原生页面(如方法中)需要弹出自定义弹窗时
import { ComponentContent } from '@kit.ArkUI'
import { BusinessError } from '@kit.BasicServicesKit';
import { WindowUtils } from '../common/constants/WindowUtils';
import Logger from '../common/utils/Logger';
let contentNode: ComponentContent<Content> | undefined
export interface Contents {
title: string
message: string
confirm?: () => void,
leftText?: string
rightText?: string
}
export async function H5ShowCustomDialog(content: Contents) {
let uiContext = WindowUtils.mainWindow.getUIContext();
let promptAction = uiContext.getPromptAction();
contentNode = new ComponentContent(uiContext, wrapBuilder(customDialogComponent), content);
try {
promptAction.openCustomDialog(contentNode)
} catch (error) {
let message = (error as BusinessError).message;
let code = (error as BusinessError).code;
Logger.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
}
}
export function H5CloseCustomDialog() {
let uiContext = WindowUtils.mainWindow.getUIContext();
let promptAction = uiContext.getPromptAction();
try {
promptAction.closeCustomDialog(contentNode)
} catch (error) {
let message = (error as BusinessError).message;
let code = (error as BusinessError).code;
Logger.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
} finally {
contentNode = undefined
}
}
@Builder
function
customDialogComponent(content: Contents) {
Column() {
Dialog({
content: content,
})
}
.width('65%')
.height(150)
.borderRadius(15)
.justifyContent(FlexAlign.Center)
.backgroundColor(Color.White)
}
@Component
export struct Dialog {
content: Contents = {} as Contents
build() {
Column() {
Text(this.content.title)
.fontSize(18)
.fontColor(Color.Black)
.fontWeight(700)
.margin({ top: 10 })
Text(this.content.message)
.fontSize(16)
.fontColor($r('app.color.font_333333'))
.margin({ top: 10, bottom: 10 })
//适用于非原生页面的两种提示框,传confirm提供确认和取消键,否则只提供确认键
if (this.content.confirm) {
Flex({ justifyContent: FlexAlign.SpaceAround }) {
Button(this.content.leftText ? this.content.leftText : '取消')
.onClick(() => {
H5CloseCustomDialog()
}).backgroundColor(0xffffff)
.fontColor(Color.Gray)
Button(this.content.rightText ? this.content.rightText : '确定')
.onClick(() => {
if (this.content.confirm) {
this.content.confirm()
}
}).backgroundColor(0xffffff)
.fontColor($r('app.color.font_0086f3'))
}.margin({ bottom: 4 })
} else {
Button('确定')
.onClick(() => {
H5CloseCustomDialog()
}).backgroundColor(0xffffff)
.fontColor($r('app.color.font_0086f3'))
}
}.justifyContent(FlexAlign.SpaceBetween).width('100%').height('100%')
}
}
调用封装好的方法即可
第一种样式
let content: Contents = {
title: '提示',
message: e,
}
H5ShowCustomDialog(content)
第二种样式
const confirm: () => void = () => {
Toolkit.cleanAppCache()
H5CloseCustomDialog()
}
let cache = (bundleStats.cacheSize / 1024 / 1024)
let content: Contents = {
title: '温馨提示',
message: `缓存大小:${cache > 1 ? cache.toFixed(2) + 'MB' : (cache * 1024).toFixed() + 'KB'}`,
rightText:'确认清理',
confirm: confirm
}
H5ShowCustomDialog(content)