> 技术文档 > HarmonyOS开发案例:优雅的封装一个自定义UI弹窗工具类(Dialog/PopWindow/多层嵌套弹窗)_鸿蒙arkts 使用自定义弹窗 封装一个弹窗组件

HarmonyOS开发案例:优雅的封装一个自定义UI弹窗工具类(Dialog/PopWindow/多层嵌套弹窗)_鸿蒙arkts 使用自定义弹窗 封装一个弹窗组件


作者寄语:

Hello,World!与时俱进,共创HarmonyOS生态,大家好,我是gelivation。

本次分享的弹窗工具类优点:可嵌套弹窗,自定义UI布局,脱离了原生组件,比较灵活,开发时自由度更高,扩展性更好。


插个题外话:ArkTS高性能之变量声明

1. 优先使用const声明常量

通过const保证地址不会发生变化

声明为const的是引用类型,引用类型内部的属性是允许改变的

对于不存在地址变化的情况下,建议使用const声明

就是讲 如果在方法中需要一个临时的对象,这个时候可以创建const的对象

2. 指定number类型

编译器会区分int和double类型,初始化number类型的变量的时候,如果预期是整数类型就初始化为0,小数类型就初始化0.0, 避免将一个number类型初始化为 nudefined 或者 null

3. 减少使用ESObject

ESObject主要用于ArkTS和TS/JS 交互的场景中作为 类型标注,在非跨语言场景中使用ESobject标注类型,会引入不必要的跨语言调用,造成额外的性能开销。

在使用ESObject时, 引入明确的 注释


正题:封装自定义UI弹窗工具类(Dialog/PopWindow/嵌套弹窗)

先看效果(扩展: 嵌套弹窗)

点击我同意:弹出另外的弹窗

点击不同意:关闭当前弹窗


代码示例

1. 将context存储到全局

UIContext 是鸿蒙ArkUI中管理UI组件上下文的核心对象,承载动画控制、字体管理、路由操作等能力。

在Ability中的onWindowStageCreate中:

//window.getLastWindow(this.context) 用于获取当前窗口//通过窗口对象获取 UIContext window.getLastWindow(this.context).then((data: window.Window) => { //通过 getUIContext() 获取上下文 let uiContext = data.getUIContext() //将uiContext存储到全局状态管理 AppStorage.setOrCreate(\'uiContext\', uiContext)})

(当然可以封装一个更好的context管理类,demo我随意一点^_^)

2. 封装PopViewUtil管理类
import { ComponentContent, promptAction } from \'@kit.ArkUI\'/** * popWindow 类型 */export enum PopViewShowType { OPEN, CLOSE}/** * popWindow ViewModel */interface PopViewModel { com: ComponentContent //关联的组件内容,自定义view组件 popType: PopViewShowType //popWindow 类型}/** * popWindow 工具类 */export class PopViewUtil { //单例 private static popView: PopViewUtil //多嵌套弹窗List,方便管理 private infoList: PopViewModel[] = new Array() static getInstance(): PopViewUtil { if (!PopViewUtil.popView) { PopViewUtil.popView = new PopViewUtil() } return PopViewUtil.popView } /** * 显示对话框 * @param type * @param contentView 自定义view * @param args 回调函数,如点击 * @param options 对话框基础选项,如dialog显示屏幕的位置等 */ static showDialog(type: PopViewShowType, contentView: WrappedBuilder, args: T, options?: promptAction.BaseDialogOptions):void { //通过AppStorage获取UiContext let uiContext = AppStorage.get(\'uiContext\') //存在 if (uiContext) { // 获取 promptAction 对象 获取弹窗操作控制器 let prompt = uiContext.getPromptAction() //构建弹窗对象 let componentContent = new ComponentContent(uiContext, contentView, args) //promptAction.BaseDialogOptions:鸿蒙弹窗组件的配置接口类型,用于定义弹窗的基本显示属性(如位置、尺寸等) //alignment: 控制弹窗在屏幕上的对齐方式 let customOptions: promptAction.BaseDialogOptions = { alignment: options?.alignment || DialogAlignment.Bottom } // 用 openCustomDialog 打开 dialog prompt.openCustomDialog(componentContent, customOptions) // 构建当前生成的dialog对象 let info: PopViewModel = { com: componentContent, popType: type } //存入单例类,方便管理 PopViewUtil.getInstance().infoList = [ ...PopViewUtil.getInstance().infoList, info ] } } static closeDialog(type: PopViewShowType): void { //通过AppStorage获取UiContext let uiContext = AppStorage.get(\'uiContext\') if (uiContext) { // 获取 promptAction 对象 获取弹窗操作控制器 let prompt = uiContext.getPromptAction() //筛选同类型弹窗 let sameTypeList = PopViewUtil.getInstance().infoList.filter((model) => { return model.popType === type }) //获取最顶层dialog let info = sameTypeList[sameTypeList.length - 1] if (info.com) { //更新弹窗list PopViewUtil.getInstance().infoList = PopViewUtil.getInstance().infoList.filter((model) => { return model.com !== info.com }) // 用 closeCustomDialog 关闭 dialog prompt.closeCustomDialog(info.com) //释放资源,防止内存泄漏 info.com.dispose() } } } //封装一层 static showPopView(contentView: WrappedBuilder, args: T, options?: promptAction.BaseDialogOptions):void { PopViewUtil.showDialog(PopViewShowType.OPEN, contentView, args, options) } //封装一层 static closePopView():void { PopViewUtil.closeDialog(PopViewShowType.OPEN) }}
3. 自定义view组件(隐私弹窗)
import { PopViewUtil } from \"./PopWindowUtil\"@Builderexport function privacyPolicyDialog(_param: ()=> void) { Column() { Text(\'服务协议及隐私政策提示\') .fontSize(\'14fp\') .margin({ top: 30, bottom: 30 }) Text(\"欢迎使用XX!为了向您全面提供内容和社区等服务,开眼将在一定情况下收集、使用和保护您的个人信息。请充分阅读并同意《用户协议》和《隐私政策》的全面内容,并基于您的真实需求使用开眼服务。\\n阅读协议中如果您不同意相关协议或其中任何条款,请停止登录程序。\\n感谢您的信任和青睐!\\n\") .fontSize(\'14fp\') .padding({ left: (34), right: (34) }) .lineHeight(24) Line() .height((1)) .width(\'100%\') .backgroundColor(\'#80C7C7C7\') Row() { Text(\'不同意\') .fontColor(\'#C7C7C7\') .fontSize(\'14fp\') .width(\'49%\') .textAlign(TextAlign.Center) .onClick(() => { PopViewUtil.closePopView() }) Line() .width(1) .height(\'100%\') .backgroundColor(\'#80C7C7C7\') Text(\'我同意\') .fontSize(\'14fp\') .width(\'49%\') .textAlign(TextAlign.Center) .onClick(() => { _param() // demo 把close方法放在《不同意》选项里面,这里为了展示嵌套view // PopViewUtil.closePopView() }) } .justifyContent(FlexAlign.SpaceAround) .width(\'100%\') .height((50)) } .width((350)) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .backgroundColor(Color.White) .borderRadius((5))}
 4. 使用
import { PopViewUtil } from \'../PopWindowUtil\';import { privacyPolicyDialog } from \'../privacyPolicyDialog\';import { promptAction } from \'@kit.ArkUI\';@Entry@ComponentV2struct Index { @Local message: string = \'Hello World\'; //我同意 回调函数 继续弹出新弹窗 agree(): () => void { return () => { promptAction.showToast({ message: \'同意隐私\', duration: 2000 }) // 模拟多弹窗 PopViewUtil.showPopView void>(wrapBuilder(privacyPolicyDialog), this.agree(), { alignment: DialogAlignment.Center }) } } build() { RelativeContainer() { Text(this.message) .id(\'HelloWorld\') .fontSize($r(\'app.float.page_text_font_size\')) .fontWeight(FontWeight.Bold) .alignRules({ center: { anchor: \'__container__\', align: VerticalAlign.Center }, middle: { anchor: \'__container__\', align: HorizontalAlign.Center } }) .onClick(() => { // 点击后弹出 PopViewUtil.showPopView void>(wrapBuilder(privacyPolicyDialog), this.agree(), { alignment: DialogAlignment.Center }) }) } .height(\'100%\') .width(\'100%\') }}

原理刨析

核心技术点

通过PopViewModel 关联 自定义view组件

在PopViewUtil中 用单例和list,方便管理已展示的弹窗列表

封装showDialog和close方法

单例方便及时更新管理list展示弹窗列表

扩展回调,要求param是一个对象,更通用,更方便灵活


结语

感谢大家观看,希望通过这次分享,大家能够学到一些有价值的技术,提升自己的技能水平。

我是gelivation,下期再见,拜拜~