自定义弹窗是 HarmonyOS 提供的一种弹窗组件,可通过CustomDialogController类来显示。以下是其简易使用方法:

1. 基本概念

从 API Version 7 开始支持,在元服务中从 API version 11 开始支持。它允许开发者自定义弹窗的样式与内容,适用于简单提示场景,但不能替代页面。

2. 重要接口及参数

  • constructor(value: CustomDialogControllerOptions)

    :配置自定义弹窗参数,所有参数不支持动态刷新。

    • builder:必填,自定义弹窗内容构造器。使用回调函数作为入参时注意this绑定问题,监听数据变化用@Link
    • cancel:可选,返回、ESC 键和点击遮障层弹窗退出时的回调。
    • autoCancel:可选,默认true,是否允许点击遮障层退出。
    • alignment:可选,弹窗竖直方向对齐方式,默认DialogAlignment.Default
    • offset:可选,弹窗相对alignment所在位置偏移量,默认{ dx: 0, dy: 0 }
    • customStyle:可选,控制弹窗容器样式是否自定义,影响圆角、宽度高度等设置。
    • gridCount:可选,弹窗宽度占栅格宽度个数,默认自适应。
    • 还有更多参数如maskColor(蒙层颜色)、maskRect(遮蔽层区域)、openAnimation(弹出动画)、closeAnimation(关闭动画)等,从 API version 10 或 12 开始支持,用于更精细的弹窗样式和行为控制。

    3. 使用步骤

  • 创建弹窗内容构造器

    • 使用@CustomDialog@Component装饰器定义结构体,在build方法中构建弹窗内容,如文本、按钮等。
  • 配置弹窗参数并创建CustomDialogController实例

    • 在父组件中创建CustomDialogController实例,传入包含builder及其他可选参数的配置对象。
  • 显示和关闭弹窗

    • 通过CustomDialogControlleropen方法显示弹窗,close方法关闭弹窗。在弹窗内容构造器中或父组件中响应按钮点击等事件来调用这些方法。

    4. 示例代码解读

    以下是示例代码片段及解释:

    // 定义第二个弹窗结构体
    @CustomDialog
    struct CustomDialogExampleTwo {
    controllerTwo?: CustomDialogController
    build() {
      Column() {
        Text('我是第二个弹窗')
        .fontSize(30)
        .height(100)
        Button('点我关闭第二个弹窗')
        .onClick(() => {
            // 关闭第二个弹窗
            if (this.controllerTwo!= undefined) {
              this.controllerTwo.close()
            }
          })
        .margin(20)
      }
    }
    }

// 定义第一个弹窗结构体,包含第二个弹窗的控制器
@CustomDialog
@Component
struct CustomDialogExample {
@Link textValue: string
@Link inputValue: string
dialogControllerTwo: CustomDialogController | null = new CustomDialogController({

builder: CustomDialogExampleTwo(),
alignment: DialogAlignment.Bottom,
onWillDismiss:(dismissDialogAction: DismissDialogAction)=> {
  // 处理弹窗即将关闭的回调
  console.info("reason=" + JSON.stringify(dismissDialogAction.reason))
  console.log("dialog onWillDismiss")
  if (dismissDialogAction.reason == DismissReason.PRESS_BACK) {
    dismissDialogAction.dismiss()
  }
  if (dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE) {
    dismissDialogAction.dismiss()
  }
},
offset: { dx: 0, dy: -25 } 

})
controller?: CustomDialogController
cancel: () => void = () => {}
confirm: () => void = () => {}
build() {

Column() {
  Text('Change text').fontSize(20).margin({ top: 10, bottom: 10 })
  TextInput({ placeholder: '', text: this.textValue }).height(60).width('90%')
  .onChange((value: string) => {
      this.textValue = value
    })
  Text('Whether to change a text?').fontSize(16).margin({ bottom: 10 })
  Flex({ justifyContent: FlexAlign.SpaceAround }) {
    Button('cancel')
    .onClick(() => {
        if (this.controller!= undefined) {
          this.controller.close()
          this.cancel()
        }
      }).backgroundColor(0xffffff).fontColor(Color.Black)
    Button('confirm')
    .onClick(() => {
        if (this.controller!= undefined) {
          this.inputValue = this.textValue
          this.controller.close()
          this.confirm()
        }
      }).backgroundColor(0xffffff).fontColor(Color.Red)
  }.margin({ bottom: 10 })
  Button('点我打开第二个弹窗')
  .onClick(() => {
      if (this.dialogControllerTwo!= null) {
        this.dialogControllerTwo.open()
      }
    })
  .margin(20)
}.borderRadius(10)

}
}

// 入口组件,包含第一个弹窗的控制器
@Entry
@Component
struct CustomDialogUser {
@State textValue: string = ''
@State inputValue: string = 'click me'
dialogController: CustomDialogController | null = new CustomDialogController({

builder: CustomDialogExample({
  cancel: ()=> { this.onCancel() },
  confirm: ()=> { this.onAccept() },
  textValue: $textValue,
  inputValue: $inputValue
}),
cancel: this.exitApp,
autoCancel: true,
onWillDismiss:(dismissDialogAction: DismissDialogAction)=> {
  console.info("reason=" + JSON.stringify(dismissDialogAction.reason))
  console.log("dialog onWillDismiss")
  if (dismissDialogAction.reason == DismissReason.PRESS_BACK) {
    dismissDialogAction.dismiss()
  }
  if (dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE) {
    dismissDialogAction.dismiss()
  }
},
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -20 },
gridCount: 4,
customStyle: false,
cornerRadius: 10,

})
// 在组件即将销毁时将弹窗控制器置空
aboutToDisappear() {

this.dialogController = null 

}
onCancel() {

console.info('Callback when the first button is clicked')

}
onAccept() {

console.info('Callback when the second button is clicked')

}
exitApp() {

console.info('Click the callback in the blank area')

}
build() {

Column() {
  Button(this.inputValue)
  .onClick(() => {
      if (this.dialogController!= null) {
        this.dialogController.open()
      }
    }).backgroundColor(0x317aff)
}.width('100%').margin({ top: 5 })

}
}


在这个示例中:

- 定义了两个弹窗结构体`CustomDialogExample`和`CustomDialogExampleTwo`,`CustomDialogExample`中包含`CustomDialogExampleTwo`的控制器,实现了弹窗嵌套。
- 在`CustomDialogExample`和`CustomDialogUser`中分别配置了弹窗参数,如对齐方式、偏移量、网格数、圆角半径等。
- 通过按钮的点击事件来控制弹窗的打开和关闭,同时处理了弹窗关闭的回调和即将关闭的拦截操作。

### 5. 注意事项

- 弹窗在避让软键盘时到达极限高度后会压缩高度,但高度压缩生效在外层容器上,若子组件设置较大固定高度,可能存在超出屏幕显示情况。
- ArkUI 弹窗在页面路由跳转时,若未手动关闭,不会自动关闭,如需同步关闭,建议使用`Navigation`。

flfljh
1 声望1 粉丝