做业务开发,弹窗几乎是绕不开的组件。确认提示、表单填写、选择器、消息提醒,全都要用弹窗。很多新手一开始只会用AlertDialog简单提示框,一旦遇到复杂需求,比如弹窗里面放图片、输入框、多行按钮,原生 AlertDialog 就不够用了。

网上不少旧版本鸿蒙的弹窗代码,拿到鸿蒙 7 直接报错。有的人直接用全屏页面模拟弹窗,写出来遮罩层、动画、关闭逻辑一堆 bug,点击遮罩无法关闭,弹窗层级错乱,多次打开还会重复创建实例,内存占用越来越高。

今天就把鸿蒙 7 里两种弹窗方案一次性讲清楚:系统简易弹窗 AlertDialog,还有业务开发最常用的@CustomDialog自定义弹窗。代码全部可直接运行,顺带讲清楚弹窗生命周期、遮罩控制、弹窗传参,以及高频踩坑点。

一、分清两种弹窗,按需选择

  1. AlertDialog:系统自带简易弹窗。适合纯文字提示、简单确认取消按钮。优点:一行代码快速拉起,不用写额外组件;缺点:布局固定,不能自由放输入框、图片、复杂 UI。
  2. @CustomDialog:自定义弹窗。完全自由写内部 UI,支持输入框、列表、图片、多按钮。正式项目复杂弹窗首选。

类比理解:AlertDialog 相当于外卖现成快餐,拿来直接吃;@CustomDialog 是你自己买菜做饭,布局、样式、交互全部自定义,自由度更高。

误区提醒:不要用 Page 页面模拟弹窗。自己写的遮罩层很难处理层级、动画、键盘避让,鸿蒙 7 提供的 @CustomDialog 底层已经帮你处理好这些问题,稳定性远高于自己手写。

1. 最简系统弹窗 AlertDialog

简单提示场景,直接调用系统弹窗。

​
import { AlertDialog } from '@kit.ArkUI';

@Entry
@Component
struct AlertDialogDemoPage {
  build() {
    Column() {
      Button("打开系统确认弹窗")
        .onClick(() => {
          AlertDialog.show({
            title: "操作确认",
            message: "确定要提交本次修改吗?",
            primaryButton: {
              value: "确认",
              action: () => {
                console.info("用户点击确认")
              }
            },
            secondaryButton: {
              value: "取消",
              action: () => {
                console.info("用户点击取消")
              }
            }
          })
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

​
关键语句拆解

AlertDialog.show():静态方法,直接唤起系统弹窗,不需要提前创建实例。

  • title:弹窗标题;message:弹窗正文文本;
  • primaryButton:主按钮,一般是确认;secondaryButton:次要按钮,一般是取消。
执行效果

点击按钮,页面中间弹出系统样式弹窗,有标题、文字、两个按钮,点击按钮自动关闭弹窗。

局限性

弹窗内部布局固定,没法自定义插入 TextInput、图片、多行列 UI。复杂表单弹窗,必须用 @CustomDialog。

二、@CustomDialog 基础自定义弹窗

@CustomDialog 是鸿蒙 7 专门用来定义自定义弹窗的装饰器,把弹窗 UI 单独封装。弹窗的打开关闭依靠DialogController控制器。

完整基础示例:

​
import { CustomDialog, DialogController } from '@kit.ArkUI';

// 定义自定义弹窗
@CustomDialog
struct SimpleCustomDialog {
  // 弹窗控制器,框架自动注入
  controller: DialogController;
  // 外部传入弹窗标题
  @Prop dialogTitle: string;

  build() {
    Column() {
      Text(this.dialogTitle)
        .fontSize(22)
        .fontWeight(FontWeight.Medium)
        .margin({bottom:16})
      Text("这是自定义弹窗,布局可以完全自由编写")
        .fontSize(16)
        .fontColor("#666")
      Row() {
        Button("关闭弹窗")
          .onClick(() => {
            // 控制器关闭弹窗
            this.controller.close()
          })
      }
      .margin({top:20})
    }
    .padding(24)
    .width("85%")
    .borderRadius(16)
    .backgroundColor("#ffffff")
  }
}

@Entry
@Component
struct CustomDialogPage {
  // 创建弹窗控制器
  @State dialogCtrl: DialogController = new DialogController();

  build() {
    Column() {
      Button("打开自定义弹窗")
        .onClick(() => {
          this.dialogCtrl.open()
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

​
关键说明

@CustomDialog:标记这个结构体是弹窗组件,和 @Component 不一样,弹窗组件必须依赖 DialogController。 controller: DialogController; 固定写法,框架自动注入,弹窗内部调用this.controller.close()关闭弹窗。 this.dialogCtrl.open():页面调用控制器 open,唤起弹窗。

运行效果:点击按钮,弹出圆角白色自定义弹窗,点击弹窗内按钮可以关闭。

三、弹窗传参 + 回调:弹窗内部操作,通知父页面

实际业务弹窗,经常需要弹窗输入内容,点击确认后把输入内容回传给页面。 思路:父页面传回调函数到弹窗,弹窗内用户操作完成,调用回调,同时关闭弹窗。

完整带输入框弹窗示例:

​
import { CustomDialog, DialogController } from '@kit.ArkUI';

@CustomDialog
struct InputCustomDialog {
  controller: DialogController;
  @Prop dialogTitle: string;
  // 回调,把输入的文本传回父页面
  @Param onConfirm: (inputText:string)=>void;
  @State inputVal: string = "";

  build() {
    Column() {
      Text(this.dialogTitle)
        .fontSize(22)
        .margin({bottom:16})
      TextInput({text:this.inputVal, placeholder:"请输入内容"})
        .onChange((val:string)=>{
          this.inputVal = val
        })
        .border({width:1, color:"#ddd"})
        .padding(8)
      Row() {
        Button("取消")
          .onClick(()=>{
            this.controller.close()
          })
        Button("确认")
          .onClick(()=>{
            // 执行回调,把输入内容传给父页面
            this.onConfirm(this.inputVal)
            this.controller.close()
          })
      }
      .margin({top:20})
      .gap(12)
    }
    .padding(24)
    .width("85%")
    .borderRadius(16)
    .backgroundColor("#fff")
  }
}

@Entry
@Component
struct InputDialogPage {
  @State dialogCtrl: DialogController = new DialogController();
  @State showResult:string = "等待弹窗输入";

  build() {
    Column() {
      Text(this.showResult).fontSize(20).margin({bottom:20})
      Button("打开输入弹窗")
        .onClick(() => {
          this.dialogCtrl.open({
            dialogTitle:"信息填写",
            onConfirm: (text:string)=>{
              this.showResult = `你输入的内容:${text}`
            }
          })
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .padding(20)
  }
}

​
重点讲解

弹窗打开的时候,open({参数}),可以一次性把 @Prop、@Param 回调传给弹窗。 弹窗内部收集用户输入,点击确认,执行父页面传入的回调函数,完成数据回传,再关闭弹窗。

四、弹窗常用配置:遮罩、点击遮罩关闭、弹窗动画

打开弹窗时,可以额外配置弹窗行为,在 open 的第二个参数传入。

​
this.dialogCtrl.open({
  dialogTitle:"提示"
},{
  mask: true, // 是否显示遮罩层
  maskClickClose: true, // 点击遮罩区域自动关闭弹窗
  alignment: DialogAlignment.Center, // 弹窗居中
  autoCancel: true
})

​
  • maskClickClose:日常需求很常用,打开之后点击弹窗外面灰色区域直接关闭弹窗;
  • alignment:支持居中、底部弹出 DialogAlignment.Bottom,做底部弹窗。

底部弹窗小提示:如果弹窗从底部弹出,记得给弹窗设置圆角,只设置顶部两个圆角,视觉更符合移动端习惯。

五、弹窗高频踩坑清单

坑 1:多次点击按钮,重复打开弹窗

连续快速点击打开弹窗按钮,会弹出多层弹窗。 解决办法:增加状态标记,弹窗打开时锁定按钮,弹窗关闭之后再解锁。

坑 2:弹窗内 @State 状态不会重置

弹窗关闭再重新打开,里面输入框还保留上次输入内容。 原因:DialogController 实例不会销毁,状态保留。解决:弹窗打开时重置变量,或者在 onWillOpen 生命周期里清空数据。

坑 3:键盘弹出遮挡弹窗输入框

不要自己手动计算位置,@CustomDialog 自带键盘避让逻辑,优先使用官方弹窗,不要手写页面模拟弹窗。

坑 4:混淆 @Component 和 @CustomDialog

把 @Component 结构体当做弹窗传入 DialogController,直接编译报错。弹窗组件必须用 @CustomDialog 装饰。

坑 5:弹窗回调丢失,忘记传参

open 的时候,没有传递 @Param 回调,弹窗点击确认调用 onConfirm 会直接崩溃。

六、弹窗选型速查表

弹窗类型API适用场景
简单提示确认框AlertDialog.show纯文本、少量按钮,不需要复杂 UI
自由布局弹窗@CustomDialog + DialogController表单、图片、多组件复杂弹窗
底部弹窗@CustomDialog + alignment:DialogAlignment.Bottom底部选择菜单、操作面板

结尾总结

鸿蒙 7 弹窗分两大类,简单提示直接用 AlertDialog,复杂 UI 就上 @CustomDialog。

  1. @CustomDialog 用来定义弹窗,依靠 DialogController 的 open/close 控制弹窗显示隐藏;
  2. open 方法可以给弹窗传参数、传入回调函数,实现父子弹窗数据通信;
  3. 支持遮罩、点击遮罩关闭、设置弹窗弹出位置,直接配置即可;
  4. 注意连续点击重复弹窗、弹窗状态残留两个高频 bug;
  5. 业务开发不要自己写页面模拟弹窗,官方弹窗底层已经处理好动画、键盘、层级。

实操指南

基于本篇代码,写一个底部弹出的自定义弹窗,弹窗内部放两个选项按钮,点击选项关闭弹窗并且把选中的内容回传给页面。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐