鸿蒙next openCustomDialog 自定义弹窗进阶版 再战江湖

357 阅读4分钟
前言导读

之前我写过一篇关于 openCustomDialog 自定义弹窗的文档 ,但是在后面的开发过程中发现这个管理起来特别麻烦,所以我就写了这个进阶版本,能够更好管理弹窗的现实和隐藏 以及弹窗清栈操作等等。 使用 BaseDialog + DialogManager  来管理的我们的弹窗

效果图

image.png

用到的技术点

openCustomDialog 来实现,自定义弹窗 openCustomDialog 可以看之前的文章

鸿蒙next openCustomDialog 实现不依赖UI的自定义弹窗

HarmonyOS 弹窗管理系统使用文档

一、概述

本弹窗管理系统采用 BaseDialog + DialogManager 架构,实现了弹窗的统一管理、复用和扩展。

架构特点

  • 单例模式:DialogManager 采用单例设计,全局统一管理
  • 泛型支持:BaseDialog 支持泛型参数,灵活适配不同弹窗
  • 弹窗栈管理:使用栈结构管理弹窗层级,支持多弹窗叠加
  • 自动适配:自动检测屏幕方向,适配横竖屏布局

二、文件结构

pages/
├── BaseDialog.ets          # 弹窗基类
├── DialogManger.ets        # 弹窗管理器
└── PrivacyPolicyDialog.ets # 隐私政策弹窗示例

三、核心类说明

3.1 BaseDialog

弹窗基类,封装通用弹窗逻辑。

方法参数返回值说明
show(params, options?)params: T, options?: DialogOptionsPromise显示弹窗
hide()-Promise隐藏弹窗
update(params)params: Tvoid更新弹窗内容
getIsShowing()-boolean获取弹窗显示状态
getDialogType()-DialogType获取弹窗类型

3.2 DialogManager

弹窗管理器,统一管理所有弹窗。

方法参数返回值说明
getInstance()-DialogManager获取单例实例
init()-DialogManager初始化管理器
registerDialog(type, builder)DialogType, WrappedBuilderDialogManager注册弹窗
show(type, params?, options?)DialogType, T?, DialogOptions?Promise显示弹窗
hide(type)DialogTypePromise隐藏弹窗
hideAll()-Promise隐藏所有弹窗
hideTop()-Promise隐藏最顶层弹窗
isShowing(type)DialogTypeboolean检查弹窗是否显示
hasDialogShowing()-boolean检查是否有弹窗显示

3.3 DialogType 枚举

export enum DialogType {
  PRIVACY_POLICY = 'privacy_policy',  // 隐私政策弹窗
  USER_PROTOCOL = 'user_protocol',    // 用户协议弹窗
  EXIT_CONFIRM = 'exit_confirm',      // 退出确认弹窗
  LOADING = 'loading',                // 加载弹窗
  ALERT = 'alert',                    // 提示弹窗
  CONFIRM = 'confirm',                // 确认弹窗
  CUSTOM = 'custom'                   // 自定义弹窗
}

3.4 DialogOptions 配置

export interface DialogOptions {
  alignment?: DialogAlignment;           // 弹窗位置,默认 Center
  isModal?: boolean;                     // 是否模态,默认 true
  autoCancel?: boolean;                  // 点击遮罩是否关闭,默认 false
  keyboardAvoidMode?: KeyboardAvoidMode; // 键盘避让模式,默认 NONE
  maskColor?: ResourceColor;             // 遮罩颜色
  onWillDismiss?: (action) => void;      // 弹窗即将关闭回调
}

四、使用示例

4.1 初始化(在 EntryAbility 中)

import { DialogManager } from '../pages/DialogManger';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 初始化弹窗管理器
    DialogManager.getInstance().init();
  }
}

4.2 显示/隐藏弹窗

import { DialogManager } from '../pages/DialogManger';
import { DialogType } from '../pages/BaseDialog';

// 方式一:使用便捷方法
await DialogManager.getInstance().showPrivacyPolicyDialog();
await DialogManager.getInstance().hidePrivacyPolicyDialog();

// 方式二:使用通用方法
await DialogManager.getInstance().show(DialogType.PRIVACY_POLICY);
await DialogManager.getInstance().hide(DialogType.PRIVACY_POLICY);

// 方式三:带参数显示
const params = new PrivacyPolicyParams();
params.title = "自定义标题";
await DialogManager.getInstance().show(DialogType.PRIVACY_POLICY, params);

4.3 自定义弹窗配置

import { DialogOptionsImpl } from '../pages/BaseDialog';

const options = new DialogOptionsImpl();
options.alignment = DialogAlignment.Bottom;
options.isModal = true;
options.autoCancel = true;
options.maskColor = '#80000000';

await DialogManager.getInstance().show(DialogType.PRIVACY_POLICY, params, options);

4.4 批量关闭弹窗

// 关闭所有弹窗
await DialogManager.getInstance().hideAll();

// 关闭最顶层弹窗
await DialogManager.getInstance().hideTop();

4.5 查询弹窗状态

// 检查特定弹窗是否显示
if (DialogManager.getInstance().isShowing(DialogType.PRIVACY_POLICY)) {
  console.log('隐私政策弹窗正在显示');
}

// 检查是否有任何弹窗显示
if (DialogManager.getInstance().hasDialogShowing()) {
  console.log(`当前有 ${DialogManager.getInstance().getShowingCount()} 个弹窗`);
}

// 获取所有正在显示的弹窗类型
const showingDialogs = DialogManager.getInstance().getShowingDialogs();

五、自定义弹窗开发指南

5.1 创建弹窗参数类

import { IBaseDialogParams } from './BaseDialog';

export class MyDialogParams implements IBaseDialogParams {
  flag?: boolean;           // 必须:屏幕方向标志
  title?: string;           // 自定义参数
  content?: string;
  onConfirm?: () => void;
  onCancel?: () => void;
  
  constructor() {
    this.title = "默认标题";
  }
}

5.2 创建弹窗组件

@Component
export struct MyDialog {
  param: MyDialogParams = new MyDialogParams();

  build() {
    Column() {
      Text(this.param.title)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Text(this.param.content)
        .fontSize(16)
        .margin({ top: 10 })
      
      Row() {
        Button('取消')
          .onClick(() => {
            DialogManager.getInstance().hide(DialogType.CUSTOM);
            this.param.onCancel?.();
          })
        
        Button('确认')
          .onClick(() => {
            DialogManager.getInstance().hide(DialogType.CUSTOM);
            this.param.onConfirm?.();
          })
      }
      .margin({ top: 20 })
    }
    .width(this.param.flag ? '80%' : '40%')  // 横竖屏适配
    .padding(20)
    .backgroundColor(Color.White)
    .borderRadius(12)
  }
}

@Builder
export function myDialogBuilder(params: MyDialogParams) {
  MyDialog({ param: params })
}

5.3 注册弹窗

// 方式一:在 DialogManager.registerDefaultDialogs() 中添加
private registerDefaultDialogs(): void {
  this.registerDialog<PrivacyPolicyParams>(
    DialogType.PRIVACY_POLICY,
    wrapBuilder(privacyPolicyDialogBuilder)
  );
  
  // 添加自定义弹窗
  this.registerDialog<MyDialogParams>(
    DialogType.CUSTOM,
    wrapBuilder(myDialogBuilder)
  );
}

// 方式二:动态注册
DialogManager.getInstance().registerDialog<MyDialogParams>(
  DialogType.CUSTOM,
  wrapBuilder(myDialogBuilder)
);

5.4 使用自定义弹窗

const params = new MyDialogParams();
params.title = "提示";
params.content = "确定要执行此操作吗?";
params.onConfirm = () => {
  console.log('用户点击了确认');
};
params.onCancel = () => {
  console.log('用户点击了取消');
};

await DialogManager.getInstance().show(DialogType.CUSTOM, params);

六、最佳实践

6.1 在弹窗内部关闭自身

// 推荐:通过 DialogManager 关闭
DialogManager.getInstance().hide(DialogType.PRIVACY_POLICY);

// 不推荐:直接调用 promptAction(会导致状态不同步)

6.2 处理返回键

const options = new DialogOptionsImpl();
options.onWillDismiss = (action: DismissDialogAction) => {
  if (action.reason === DismissReason.PRESS_BACK) {
    // 阻止返回键关闭
    action.dismiss;
    // 或执行自定义逻辑
  }
};

6.3 弹窗生命周期管理

// 页面销毁时关闭所有弹窗
aboutToDisappear(): void {
  DialogManager.getInstance().hideAll();
}

七、注意事项

  1. 必须初始化:使用前必须调用 DialogManager.getInstance().init()
  2. 异步操作show() 和 hide() 都是异步方法,需要使用 await
  3. 参数传递:弹窗参数中的 flag 会自动设置,无需手动指定
  4. 弹窗注册:使用前必须先注册弹窗,否则会抛出异常
  5. ArkTS 兼容:代码已适配 ArkTS 严格模式,避免使用对象展开等不支持的语法

八、版本历史

版本日期说明
1.0.02025-09-03初始版本,支持基础弹窗管理
2.0.02025-09-04重构为 BaseDialog + DialogManager 架构