【HarmonyOS NEXT】实现@CustomDialog自定义弹窗代码高效复用

【问题描述】
使用鸿蒙提供的@CustomDialog,实现弹窗,每次都要写很多重复性代码,想把CustomDialogController挂载到一个单例类中,方便代码复用。发现在class中封装了CustomDialogController的创建函数,弹窗无法弹起。

【原因】
CustomDialogController需要绑定一个ownerView。CustomDialogController定义在class或者静态方法中因缺少绑定关系,与UI组件没有任何关联关系导致拉不起弹窗,所以CustomDialogController需要定义在组件内。

【解决方案】
放弃@CustomDialog自定义弹窗
改用创建子窗口的方式创建弹窗,实现代码复用

【步骤】

  1. 在EntryAbility.ets中,使用 AppStorage 缓存 windowStage
async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
  // ...
  AppStorage.setOrCreate('windowStage', windowStage);
  // ...
}
  1. 准备数据模型
interface BtnType {
  label: string
  code: string
  color?: Color | string
  autoClose?: boolean
  callback: (type: string) => void
}

interface OptionsType {
  title: string,
  btnList: BtnType[]
}
  1. 在工具模块中,实现单例类 SubWinDialog
import { window } from '@kit.ArkUI'
import { BusinessError } from '@kit.BasicServicesKit'

import { OptionsType } from '../pages/StyleDemo/models'

const windowStage: window.WindowStage | undefined = AppStorage.get('windowStage')
const mainWindowProperties = windowStage?.getMainWindowSync().getWindowProperties()
const mainWindowWidth = mainWindowProperties?.windowRect.width || 0
const mainWindowHeight = mainWindowProperties?.windowRect.height || 0

let subWindowClass: window.Window | null = null

export class SubWinDialog {
  static open(options: OptionsType) {
    if (!windowStage) return
    AppStorage.setOrCreate('windowStageOptions', options)

    // 1.创建子窗口
    windowStage.createSubWindow('mySubWindow', (err: BusinessError, data) => {
      if (err.code) return
      subWindowClass = data

      // 2.子窗口创建成功后,设置子窗口的位置、大小及相关属性等
      subWindowClass.moveWindowTo(0, 0)
      subWindowClass.resize(mainWindowWidth, mainWindowHeight)
      subWindowClass.setWindowLayoutFullScreen(true)

      // 3.为子窗口加载对应的目标页面
      subWindowClass.setUIContent('pages/StyleDemo/SubWinPage', (err: BusinessError) => {
        if (err.code) return

        // 4.显示子窗口
        (subWindowClass as window.Window).showWindow()
      })
    })
  }

  static close() {
    const win = window.findWindow('mySubWindow') // 找到子窗口
    win.destroyWindow() // 销毁窗口
  }
}
  1. 第3步创建的子窗口需要加载一个页面,这个页面的样式就是我们弹窗的样式
import { window } from '@kit.ArkUI';
import { BtnType, OptionsType } from './models';

@Entry
@Component
struct SubWinPage {
  @State message: string = 'Hello World';
  @State options: OptionsType | undefined = undefined

  aboutToAppear(): void {
    this.options = AppStorage.get('windowStageOptions');
  }

  closeWin() {
    const win = window.findWindow('mySubWindow') // 找到子窗口
    win.destroyWindow() // 销毁窗口
  }

  build() {
    Row() {
      Column() {
        Text(this.options?.title || '')
          .layoutWeight(1)
        Row() {
          if (this.options?.btnList.length) {
            ForEach(this.options.btnList, ((item: BtnType, index: number) => {
              Text(item.label)
                .border({
                  width: { left: index > 0 ? 0.5 : 0 },
                  color: Color.Gray
                })
                .testStyle()
                .fontColor(item.color || Color.Black)
                .onClick(() => {
                  item.callback(item.code)
                  item.autoClose && this.closeWin()
                })
            }))
          }

        }
        .border({
          width: { top: 0.5 },
          color: Color.Gray
        })
        .height(50)
        .width('100%')
      }
      .width('100%')
      .height(200)
      .backgroundColor(Color.White)
      .borderRadius(8)
    }
    .height('100%')
    .padding({ left: 50, right: 50 })
    .backgroundColor("#60000000")
    .onAppear(() => {
      const win = window.findWindow('mySubWindow')
      win.setWindowBackgroundColor("#00000000")
    })
  }
}

@Extend(Text)
function testStyle() {
  .width('100%')
  .height('100%')
  .textAlign(TextAlign.Center)
  .layoutWeight(1)
}
  1. 调用
import { SubWinDialog } from '../../utils'
import { promptAction } from '@kit.ArkUI'
import { BtnType } from './models'

@Entry
@Component
struct MainWinPage {
  @State message: string = ''
  btnList: BtnType[] = [{
    label: '取消',
    code: 'cancel',
    autoClose: true,
    callback: (type: string) => {
      this.message = type
    }
  }, {
    label: '验证',
    code: 'confirm',
    color: '#0a59f7',
    callback: (type: string) => {
      this.message = type
      if (Math.random() > 0.5) {
        promptAction.showToast({ message: '验证失败,请再次点击验证' })
        return
      }
      SubWinDialog.close()
    }
  }]

  build() {
    Column({ space: 20 }) {
      Text('测试弹窗页')
        .width('100%')
        .padding({ top: 50, bottom: 10 })
        .fontSize(20)
        .textAlign(TextAlign.Center)
        .fontColor(Color.White)
        .backgroundColor(Color.Green)
      Text(`点击了:${this.message}`)
      Button('显示弹窗')
        .onClick(() => {
          SubWinDialog.open({
            title: '你好',
            btnList: this.btnList
          })
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Orange)
  }
}
  1. 效果图
弹窗前 弹窗时 点击取消 点击验证
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值