OpenHarmony应用开发之自定义弹窗 原创 精华

zhushangyuan_
发布于 2023-8-5 16:21
1.2w浏览
1收藏

OpenHarmony应用开发之自定义弹窗

应用场景

在应用的使用和开发中,弹窗是一个很常见的场景,自定义弹窗又因为极高的自由度得以广泛应用。本文以橘子购物中一个应用更新提示的弹窗介绍OpenHarmony的自定义弹窗。

OpenHarmony应用开发之自定义弹窗-鸿蒙开发者社区

接口

自定义弹窗官方文档:自定义弹窗-弹窗-全局UI方法-组件参考(基于ArkTS的声明式开发范式)-ArkTS API参考-HarmonyOS应用开发

CustomDialogController是自定义弹窗对应的接口,详细介绍如下:

CustomDialogController(value:{builder: CustomDialog, cancel?: () => void, autoCancel?: boolean, alignment?: DialogAlignment, 
                      offset?: Offset, customStyle?: boolean, gridCount?: number, maskColor?: ResourceColor, 
                      openAnimation?: AnimateParam, closeAnimation?: AnimateParam})
  • 1.
  • 2.
  • 3.

参数:

参数名 参数类型 必填 参数描述
builder CustomDialog 自定义弹窗内容构造器。
cancel () => void 点击遮障层退出时的回调。
autoCancel boolean 是否允许点击遮障层退出。默认值:true
alignment DialogAlignment 弹窗在竖直方向上的对齐方式。默认值:DialogAlignment.Default
offset Offset 弹窗相对alignment所在位置的偏移量。
customStyle boolean 弹窗容器样式是否自定义。默认值:false,弹窗容器的宽度根据栅格系统自适应,
不跟随子节点;高度自适应子节点,最大为窗口高度的90%;圆角为24vp。
gridCount8+ number 弹窗宽度占栅格宽度的个数。默认为按照窗口大小自适应,异常值按默认值处理,
最大栅格数为系统最大栅格数。

这其中最重要的就是builder,我们需要自己实现一个构造器,也就是这个弹窗的页面。

具体实现

定义CustomDialogController

首先,我们需要定义一个CustomDialogController:

UpdateDialogController: CustomDialogController = new CustomDialogController({
  builder: UpdateDialog(),
  customStyle: true
})
  • 1.
  • 2.
  • 3.
  • 4.

这个CustomDialogController就代表弹窗,UpdateDialog()是弹窗的具体实现,customStyle为ture就表示弹窗样式可以自定义。

设置调用时机

在这个场景中,我们想要每次打开应用的时候弹窗,其他时候不弹窗,我们需要在首页组件的aboutToAppear中加入以下代码:

aboutToAppear() {
  if(AppStorage.Get('nowIndex') === undefined || AppStorage.Get('nowIndex') === 0){
    this.UpdateDialogController.open()
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

aboutToAppear函数的调用时机是创建自定义组件的新实例后,执行其build()函数之前,所以在首页组件的aboutToAppear加入CustomDialogController的打开开逻辑可使弹窗仅在应用打开的时候触发。

aboutToAppear参考文档:自定义组件的生命周期-组件参考(基于ArkTS的声明式开发范式)-ArkTS API参考-HarmonyOS应用开发

实现builder实例

实现实例可以直接在builder后面直接实现,也可以定义在其他文件中,然后通过调用的方式获取,本文以调用方式实现。

实例组件的定义前需加export才能暴露出去:

export struct UpdateDialog {}
  • 1.

弹窗上所需的数据和获取也需要在在此处定义:

@CustomDialog
export struct UpdateDialog {
  @State currentVersion: string = ''
  @State richTextData: string = ''
  @State lastVersion: string = ''
  @State updateContent: string = ''
  private context?: AbilityContext
  private customDialogController?: CustomDialogController

  async aboutToAppear() {
    this.context = getContext(this) as AbilityContext
    this.richTextData = await dialogFeature.getRichTextData(this.context)
    Logger.info(TAG, `this.richTextData = ${this.richTextData}`)
    await this.getData()
  }

  async getData() {
    try {
      this.currentVersion = await dialogFeature.getCurrentVersion()
      let requestResponseContent: RequestResponseContent = await dialogFeature.getLastVersion()
      if (requestResponseContent.content === null || requestResponseContent.content === undefined) {
        return
      }
      this.updateContent = requestResponseContent.content
      if (requestResponseContent.versionName === null || requestResponseContent.versionName === undefined) {
        return
      }
      this.lastVersion = requestResponseContent.versionName
    } catch (err) {
      Logger.info(TAG, `getApplicationVersion is fail`)
    }
  }
  ...
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

以上是应用升级所需的数据结构及部分数据获取。

弹窗具体实现

自定义弹窗的实现就是在原页面的基础上再加一层页面,页面内容自定义。

弹窗页面我们可以通过stack组件实现,stack组件会使容器内的子组件堆叠布局,使用stack的好处是可以添加一层遮罩效果。

Stack() {
  // mask 遮罩层
  Column()
    .width('100%')
    .height('100%')
    .backgroundColor('#000000')
    .opacity(.4)
    
...
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

以上代码在stack的第一层设置了backgroundColor和opacity属性,这样会产生如开始示意图的遮罩效果。

需要注意的是,需要在取消按钮的调用函数中关闭弹窗,具体代码如下:

Button($r('app.string.cancel'))
  .onClick(() => {
    this.customDialogController.close()
  })
  • 1.
  • 2.
  • 3.
  • 4.

弹窗完整代码:

build() {
  Stack() {
    // mask 遮罩层
    Column()
      .width('100%')
      .height('100%')
      .backgroundColor('#000000')
      .opacity(.4)
    Column() {
      Stack({ alignContent: Alignment.TopStart }) {
        Text($r('app.string.update_title'))
          .fontSize(30)
          .fontColor('#FFFFFF')
          .fontWeight(500)
          .margin({ top: 70, left: 76 })

        Text(`V${(this.lastVersion || updateData.versionName)}`)
          .fontSize(16)
          .backgroundColor('#FFFFFF')
          .textAlign(TextAlign.Center)
          .fontColor('#E9304E')
          .borderRadius(20)
          .width(80)
          .aspectRatio(2.8)
          .margin({ top: 110, left: 76 })

        Column() {
          // 富文本容器
          Scroll() {
            Column() {
              if (this.richTextData) {
                RichText((this.updateContent || this.richTextData))
                  .width('100%')
                  .height('100%')
              }
            }
            .width('100%')
          }
          .height(200)

          Row() {
            Button($r('app.string.cancel'))
              .commonButtonStyle()
              .fontSize(20)
              .margin({ left: 10 })
              .fontColor('#E92F4F')
              .backgroundColor('rgba(0,0,0,0.05)')
              .margin({ right: 10 })
              .onClick(() => {
                this.customDialogController.close()
              })
              .key("cancel")

            Button($r('app.string.update_now'))
              .commonButtonStyle()
              .fontSize(20)
              .margin({ right: 10 })
              .fontColor('#FFFFFF')
              .backgroundColor('#E92F4F')
              .margin({ left: 10 })
              .onClick(() => {
                this.customDialogController.close()
              })
              .key("Now")
          }
          .margin({ top: 30 })
        }
        .width('100%')
        .padding({ left: 25, right: 25 })
        .margin({ top: 230 })
      }
      .height(600)
      .width('100%')
      .backgroundImage($r('app.media.update'), ImageRepeat.NoRepeat)
      .backgroundImageSize(ImageSize.Contain)
    }
    .width(480)
    .padding({ left: 16, right: 16 })
  }
  .width('100%')
  .height('100%')
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.

以上是弹窗完整代码,需要注意的是,本例并未实现应用升级的具体逻辑,所以升级按钮的操作也是关闭弹窗。

参考

本文供稿:https://gitee.com/JaysonLiu3

本例参考的官方文档:橘子购物

自定义弹窗官方文档

自定义组件的生命周期-aboutToAppear

层叠布局(Stack)-构建布局-开发布局-基于ArkTS的声明式开发范式-UI开发-开发-HarmonyOS应用开发

线性布局(Row/Column)-构建布局-开发布局-基于ArkTS的声明式开发范式-UI开发-开发-HarmonyOS应用开发

按钮(Button)-添加常用组件-添加组件-基于ArkTS的声明式开发范式-UI开发-开发-HarmonyOS应用开发

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
2
收藏 1
回复
举报
2
2
1
2条回复
按时间正序
/
按时间倒序
喝一大口可乐
喝一大口可乐

如果交应用商店审核,对弹窗这些应该会有要求的吧

已于2023-8-7 10:46:16修改
回复
2023-8-7 10:46:03
zhushangyuan_
zhushangyuan_

应该没事,手机安装的很多应用都有这样的弹窗。


华为应用市场,应用上架审核要求  快速查了下 没有找到对升级弹窗的限制:​​https://developer.huawei.com/consumer/cn/doc/distribution/app/50104-01​

回复
2023-8-8 14:08:09


回复
    相关推荐