【鸿蒙开发】怎样在鸿蒙ArkTs中进行全局弹框

前言

在鸿蒙开发中,经常会遇到在一个公共的类里,会想要给当前window上添加一个全屏的自定义视图,那在鸿蒙中应该如何实现这一个效果呢?

这里介绍一下我自己想到的实现方式,不一定是最优解,大家有其他更好的方式或者问题,欢迎指正。

代码是基于鸿蒙next和模拟器

思路

在鸿蒙中,虽然可以通过下面的系统方法获取到window,但是目前我不知道如何像iOS一样,在其上添加自定义的组件。所以,在研究了系统的window之后,想到是否可以直接弹出一个全屏的window,然后在这个自定义的window上,添加我们的自定义组件。类似于iOS的三方库SwiftEntryKit

import { window } from '@kit.ArkUI'
function findWindow(name: string): Window;

实现步骤

1.通过调用createWindow函数,创建一个自定义的window,并设置windowType枚举为
TYPE_DIALOG,这个是一个API10之后有的类型。

2.通过调用loadContent(path: string, storage: LocalStorage, callback: AsyncCallback): void创建一个指定的页面作为这个window的根视图,我们后面自己的自定义弹框组件,都是加载到这个页面中。第二个参数storage也很重要,因为通过该方法指定了页面,但是无法将自定义的参数直接传入到页面中,所以通过LocalStorage进行中转传值。

3.在需要进行传值的属性中,非常重要的是一个entry?: CustomBuilder自定义组件的属性,因为我们毕竟是要封装一个通用的类,去支持你传入任意的自定义视图。这里有个非常重要的点:在class中传入的这个属性,是一个代码块,里面是我们自定义的组件代码,但是我们无法在page中,直接去执行这个代码块,来获取到相应的布局。这里其实还需要在page的代码中新增一个属性@BuilderParam entryView: CustomBuilder,这个应该很熟悉,就是如果我们是直接初始化一个包含这个属性的组件时,就可以直接传入一个@Builder function()自定义组件,并且内部可以直接使用。那我们这里需要做的就是,在page的aboutToAppear中,将我们传入的参数,赋值给这个页面声明的属性,这样就可以在布局中去加载这个布局了。

4.传入到页面中的参数,还可以包含布局/动画等参数,这里只实现了布局,后续可以继续完善动画相关方法

5.最后在传入这个布局代码的时候,如果有自定义的点击事件,需要注意this的绑定当前调用方。

代码

公共模块:

import { window } from '@kit.ArkUI'
import { common } from '@kit.AbilityKit'

export class HDEntryKit {
  static display(use: EKAttributes) {
    HDWindowProvider.instance().display(use)
  }

  static dismiss(complete?: (() => void)) {
    HDWindowProvider.instance().dismiss(complete)
  }
}

class HDWindowProvider {
  private static windowProvider: HDWindowProvider
  context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  windowName: string = "HDEntryWindow"

  static instance() {
    if (!HDWindowProvider.windowProvider) {
      HDWindowProvider.windowProvider = new HDWindowProvider();
    }
    return HDWindowProvider.windowProvider;
  }

  display(use: EKAttributes) {
    let windowClass: window.Window
    window.createWindow({
      name: this.windowName,
      windowType: window.WindowType.TYPE_DIALOG,
      ctx: this.context
    }, (err, data) => {
      if (err.code == 0) {
        windowClass = data
        windowClass.setWindowLayoutFullScreen(true)
        let bundleName = this.context.applicationInfo.name
        let page = `@bundle:${bundleName}/uicomponents/ets/HDEntryKit/HDEntryPage`
        let storage: LocalStorage = new LocalStorage()
        storage.setOrCreate('use', use)
        windowClass.loadContent(page, storage, err => {
          if (err.code == 0) {
            windowClass.setWindowBackgroundColor(use.backgroundColor?.toString())
          }
        })
        windowClass.showWindow(() => {
        })
      }
    })
  }

  dismiss(complete?: (() => void)) {
    window.findWindow(this.windowName).destroyWindow((err, e) => {
      if (err.code == 0 && complete) {
        complete()
      }
    })
  }
}

export class Size {
  width: Length | null = null
  height: Length | null = null
  margin: Length | Padding = 0
}

export class EKAttributes {
  name?: string
  entry?: CustomBuilder
  position: FlexAlign = FlexAlign.Center
  backgroundColor: ResourceColor = "#99000000"
  displayDuration: number = 1000
  size: Size = new Size()
}
 { EKAttributes, HDEntryKit } from './HDEntryKit'

let storage = LocalStorage.getShared()
@Entry(storage)
@Component
struct HDEntryPage {
  @BuilderParam entryView: CustomBuilder
  @LocalStorageProp('use') use: EKAttributes = new EKAttributes()

  build() {
    Column() {
      Row() {
        Column() {
          if (this.entryView) {
            this.entryView()
          }
        }
        .width('100%')
        .onClick(e => {})
      }
      .width(this.use.size.width)
      .height(this.use.size.height)
      .margin(this.use.size.margin)
      .backgroundColor(Color.Blue)
    }
    .width('100%')
    .height('100%')
    .justifyContent(this.use.position)
    .onClick(event => {
      HDEntryKit.dismiss()
    })
  }

  aboutToAppear(): void {
    this.entryView = this.use.entry
  }

调用方:

/// 弹框的配置
let use = new EKAttributes()
use.size.height = 100
use.size.margin = 20
use.position = FlexAlign.End
use.entry = this.text.bind(this)
HDEntryKit.display(use)

/// 自定义的弹框组件
@Builder text() {
  Row() {
    Text("123")
      .backgroundColor('#ff0000')
      .onClick(() => {
        this.test()
      })
  }
  .width('100%')
  .justifyContent(FlexAlign.Start)
}

/// 弹框组件中的页面跳转事件
test() {
  HDEntryKit.dismiss(() => {
    let bundleName = this.context.applicationInfo.name
    let loginPage = `@bundle:${bundleName}/login/ets/pages/LoginRegisterPage`
    router.pushUrl({url: loginPage})
  })
}

注意

通过自定义window方法弹出页面后,如果在调用router.push,则是默认在这个自定义的window进行页面跳转,当你销毁这个window的时候,打开的页面都会被关闭。所以,在demo里是在window销毁后,再进行页面跳转

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。随着鸿蒙的不断发展以及国家的大力支持,未来鸿蒙职位肯定会迎来一个大的爆发,只有积极应对变化,不断学习和提升自己,我们才能在这个变革的时代中立于不败之地。在这里插入图片描述

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值