【鸿蒙】自定义弹窗非置顶解决方案 - 子窗口显示

问题概述

鸿蒙自带的自定义弹窗存在一直置顶的问题,具体业务例子

在隐私弹窗的Dialog里通过router跳转到隐私协议内容页后,弹窗一直处于最外层。按照真实业务,应该是在隐私弹窗的上层压入隐私协议,退出隐私协议页面后,弹窗还在。

目前鸿蒙自带的@CustomDialog 以及ComponentContent 封装弹窗类组件两种API实现效果均是弹窗一直置顶

解决方案

目前有两种解决方案

1.子窗口: 通过创建子窗口,并在子窗口Page内加载@Builder自定义组件( 弹窗 ) —— 本文

2.关闭 - 打开:通过订阅openCustomDialogcloseCustomDialog事件来控制显隐,实现业务。

具体实现

工程结构

  • LoadContentWindow.ets // 子窗口
  • SubWindowParams.ets // 子窗口传参封装
  • SubWindowApi.ets // 子窗口创建初始化封装
  • SubWindowFunction.ets // 方法调用封装

1. 子窗口的创建初始化,以及销毁,并且订阅对应事件 - SubWindowApi.ets

const EVENT_ID = 1234565; // 持续订阅事件ID

// window实例
interface Handler {
  windowStage: window.WindowStage;
}


/**
 * 窗口的创建,加载,显示,销毁操作
 */
export class SubWindowApi {
  private subWindow: window.Window | null = null; // 初始化window实例
  private Storage: LocalStorage = new LocalStorage(); // 创建页面级UI状态存储对象
  // eventId为1234565的持续订阅的事件
  private callbackEvent: emitter.InnerEvent = {
    eventId: EVENT_ID
  }

  // 显示当前窗口
  private showSubWindow() {
    if (this.subWindow) {
      this.subWindow.showWindow((err: BusinessError) => {
        if (err.code) {
          console.error('Fail to show window, Cause: ' + JSON.stringify(err));
        }
      })
    }
  }

  // 为当前WindowStage加载命名路由页面
  private loadContent(path: string) {
    if (this.subWindow) {
      // 用loadContentByName为当前窗口加载命名路由页面,通过LocalStorage传递状态属性给加载的页面
      this.subWindow.loadContentByName(path, this.Storage, (err: BusinessError) => {
        if (err.code) {
          console.error("Failed to load the content. Cause:" + JSON.stringify(err));
          return;
        }
      });
    }
  }

  // 销毁当前窗口
  private destroySubWindow() {
    if (this.subWindow) {
      this.subWindow.destroyWindow((err) => {
        if (err.code) {
          console.error('Fail to destroy the window. Cause:' + JSON.stringify(err));
          return;
        }
        this.subWindow = null;
      });
    }
  }

  // 创建子窗口
  private createSubWindow(windowStage: window.WindowStage | null) {
    try {
      if (!windowStage) {
        return;
      }
      windowStage.createSubWindow('subDialogWindow', (err: BusinessError, data) => {
        if (err.code) {
          console.error("Failed to create the subwindow, Cause: " + JSON.stringify(err));
          return;
        }
        this.subWindow = (data as window.Window);
        if (this.subWindow) {
          // 设置子窗口尺寸
          this.subWindow.resize(
            UIUtil.getScreenWidthPx(),
            UIUtil.getScreenHeightPx())
          // 布局避让状态栏与导航栏
          this.subWindow.setWindowLayoutFullScreen(false)
          // 设置子窗口可触
          this.subWindow.setWindowTouchable(true);
          // 设置窗口UI
          this.loadContent(entryName);
          // 展示子窗口
          this.showSubWindow();
        }
      });
    } catch (exception) {
      console.error("Failed to create the window, Cause: " + JSON.stringify(exception));
    }
  }

  /**
   * 订阅eventId为1234565的事件
   */
  subscribeCallback(): void {
    emitter.on(this.callbackEvent, () => {
      this.hideSubWindow();
    })
  }

  /**
   * 取消针对eventId为1234565的事件的订阅
   */
  offCallback(): void {
    emitter.off(this.callbackEvent.eventId);
  }

  /**
   * 更新key为'ad'的变量值
   * @param { AdWindowParams } params - 页面显示的值
   */
  updateOrCreateParams(params: SubWindowParams): void {
    this.Storage.setOrCreate("subWindowParams", params);
  }

  /**
   * 创建并展示弹窗
   * @param { AdWindowParams } params - 页面显示的值
   * @param { Handler } handler - WindowStage对象
   */
  initSubWindow(handler: Handler, params: SubWindowParams): void {
    const windowStage = handler.windowStage;
    // 注册回调
    this.subscribeCallback();
    // 初始化参数
    this.updateOrCreateParams(params);
    // 新建子窗口
    this.createSubWindow(windowStage);
  }

  /**
   * 隐藏弹窗
   */
  hideSubWindow(): void {
    // 注销监听事件
    this.offCallback();
    // 关闭弹窗
    this.destroySubWindow();
  }
}

2. 创建子窗口所加载的页面 - LoadContentWindow.ets

export const entryName: string = 'LoadContent';

const EVENT_ID = 1234565; // 持续订阅事件ID
const DURATION = 400; // 动画时间
const IMAGE_SCALE = 0.8; // 弹窗画面缩放大小


@Entry({
  routeName: entryName,
  storage: LocalStorage.getShared()
})
  // window单独加载的命名路由页面: LoadContent
@Component
export struct LoadContent {
  @LocalStorageLink('subWindowParams') params: SubWindowParams | null = null;
  private windowClass: window.Window = window.findWindow('subDialogWindow');
  private event: emitter.InnerEvent = {
    eventId: EVENT_ID
  }
  aboutToAppear(): void {
    if (this.params != null) {
      this.params.onBtnCloseListener = () => {
        // 触发回调,关闭弹窗
        emitter.emit(this.event);
      }
    }
  }

  onBackPress(): boolean | void {
    //监听反回键
    emitter.emit(this.event);
    return true;
  }

  onPageHide(): void {
    //设置子窗口蒙层颜色
    try {
      this.windowClass.setWindowBackgroundColor('#ffffff');
    } catch (exception) {
      console.error('Failed to set the background color. Cause: ' + JSON.stringify(exception));
    }
  }

  onPageShow(): void {
    //设置子窗口蒙层颜色
    try {
      this.windowClass.setWindowBackgroundColor("#57050505")
    } catch (exception) {
      console.error('Failed to set the background color. Cause: ' + JSON.stringify(exception));
    }
  }

  build() {
    Column() {
      Stack() {
        if (this.params != null) {
          dialog_privacy(this.params)
        } else {
          Text("NULL")
        }
      }
    }
    .width(UIUtil.getScreenWidthVp())
    .height(UIUtil.getScreenHeightVp())
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .transition(TransitionEffect.OPACITY.animation({
      duration: DURATION,
      curve: Curve.Ease
    }).combine(TransitionEffect.scale({
      x: IMAGE_SCALE,
      y: IMAGE_SCALE
    })))
  }
}

3. 封装方法调用 - SubWindowFunction.ets

const context = getContext(this) as common.UIAbilityContext; // 获取当前页面的上下文

/**
 * 创建并展示弹窗
 * @param { api.SubWindowApi | null } SubWindowApi - SubWindowApi对象
 * @param { window.WindowStage | undefined } windowStage - WindowStage对象
 */
export function showApiSubWindow(SubWindowApi: api.SubWindowApi | null, windowStage: window.WindowStage | undefined, params: SubWindowParams) {
  SubWindowApi?.initSubWindow({ windowStage: windowStage as window.WindowStage }, params);
}

/**
 * 隐藏弹窗
 * @param { api.SubWindowApi | null } SubWindowApi - SubWindowApi对象
 */
export function hideApiSubWindow(SubWindowApi: api.SubWindowApi | null) {
  SubWindowApi?.hideSubWindow();
}

4. 自定义传参 - SubWindowParams.ets

export class SubWindowParams {
  onBtnCloseListener?: () => void
  
  constructor() {
  }

}

参考

https://gitee.com/harmonyos-cases/cases/blob/master/CommonAppDevelopment/feature/customdialog/README.md

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值