弹窗和提示气泡是应用中与用户交互最频繁的组件之一,设计是否到位直接影响用户体验。在 HarmonyOS Next 中,ArkUI 框架提供了从系统级 AlertDialog 到完全自定义的 CustomDialog,再到轻量级 Popup 和全局 Toast 的完整解决方案。本文将从应用场景出发,系统梳理各类弹框和提示气泡的实现方式、核心配置和选型建议。
一、弹框体系概览
HarmonyOS Next 的弹框提示体系可以分为三个层级,每个层级服务不同的交互场景:
| 分类 | 典型组件 | 适用场景 |
|---|---|---|
| 系统预置弹框 | AlertDialog | 操作确认、警告提示、简单选择 |
| 自定义弹框 | @CustomDialog + CustomDialogController | 包含表单输入、自定义布局、复杂交互 |
| 轻量提示 | Toast / bindPopup | 操作反馈、引导提示、补充信息 |
此外,还有 OverlayManager(浮层)、bindSheet(半模态页面)和 bindContentCover(全模态页面)等更高级的 UI 呈现方式,适用于需要独立页面级交互的场景。
二、AlertDialog:系统级警告弹窗
AlertDialog是最常用的预置弹窗,适合快速实现确认/取消类交互。通过AlertDialog.show()静态方法调用,无需提前声明组件。
基础用法
以下是一个删除确认弹窗的示例:
typescript
@Entry @Component struct AlertDialogPage { @State dialogMessage: string = '' build() { Column({ space: 10 }) { Button('删除联系人').onClick(() => { AlertDialog.show({ title: '删除联系人', message: '是否删除所选的联系人?', primaryButton: { value: '取消', action: () => { this.dialogMessage = '已取消删除' } }, secondaryButton: { value: '删除', fontColor: Color.Red, action: () => { // 执行删除逻辑 this.dialogMessage = '删除成功' } } }) }) Text('操作结果:' + this.dialogMessage) } .height('100%') .width('100%') } }核心配置参数
AlertDialog.show()支持丰富的配置项,满足大多数场景需求:
| 参数 | 类型 | 说明 |
|---|---|---|
title | string | 弹窗标题 |
subtitle | string | 副标题 |
message | string | 弹窗内容 |
autoCancel | boolean | 点击遮障层是否自动关闭,默认 true |
alignment | DialogAlignment | 垂直对齐方式,默认居中 |
gridCount | number | 弹窗宽度占栅格数,默认 4 |
cornerRadius | Dimension | 圆角半径,默认 32vp |
backgroundBlurStyle | BlurStyle | 背景模糊材质 |
onWillDismiss | () => void | 交互式关闭回调 |
primaryButton和secondaryButton支持配置按钮文字、颜色和点击回调,是操作确认类弹窗的核心配置。
三、@CustomDialog:自定义弹框
当系统预置弹窗无法满足需求时(如需要表单输入、定制布局、复杂交互),可以使用@CustomDialog装饰器构建完全自定义的弹窗。
1. 定义自定义弹窗组件
使用@CustomDialog装饰器声明自定义弹窗,内部必须声明CustomDialogController类型的控制器属性,外部通过该控制器控制弹窗的打开和关闭。
typescript
// components/CustomInputDialog.ets @CustomDialog export struct CustomInputDialog { @State inputValue: string = '' dialogController?: CustomDialogController // 外部传入的回调 cancel?: () => void confirm?: (value: string) => void build() { Column() { Text('请输入昵称') .fontSize(18) .width('100%') .textAlign(TextAlign.Center) .margin({ top: 30 }) TextInput({ text: $$this.inputValue, placeholder: '请输入昵称' }) .height(40) .margin(10) .fontSize(18) Row() { Button('取消') .layoutWeight(1) .margin(5) .backgroundColor(Color.Gray) .onClick(() => { this.dialogController?.close() this.cancel?.() }) Button('确定') .layoutWeight(1) .margin(5) .onClick(() => { this.dialogController?.close() this.confirm?.(this.inputValue) }) } .padding(10) .margin({ bottom: 10 }) } .width('100%') .backgroundColor(Color.White) .borderRadius(16) } }2. 在页面中使用自定义弹窗
页面中需要创建CustomDialogController实例,传入自定义弹窗组件并配置相关参数:
typescript
// pages/ProfilePage.ets import { CustomInputDialog } from '../components/CustomInputDialog' @Entry @Component struct ProfilePage { @State nickName: string = '默认昵称' dialogController: CustomDialogController = new CustomDialogController({ builder: CustomInputDialog({ cancel: () => { console.info('用户取消输入') }, confirm: (value: string) => { this.nickName = value console.info('新昵称:' + value) } }), alignment: DialogAlignment.Center, // 居中显示 customStyle: false, // 使用默认样式 autoCancel: true, // 点击遮障层可关闭 maskColor: 0x33000000 // 半透明遮罩 }) build() { Column({ space: 20 }) { Text('当前昵称:' + this.nickName) .fontSize(18) Button('修改昵称') .onClick(() => { this.dialogController.open() }) } .height('100%') .width('100%') } }3. CustomDialogController 核心配置
| 参数 | 类型 | 说明 |
|---|---|---|
builder | CustomBuilder | 自定义弹窗内容构造器(必填) |
alignment | DialogAlignment | 对齐方式,默认居中 |
offset | { dx, dy } | 偏移量 |
customStyle | boolean | 是否自定义容器样式 |
autoCancel | boolean | 点击遮障层是否关闭 |
maskColor | string | 遮罩颜色,默认 0x33000000 |
openAnimation | AnimationOptions | 打开动画 |
closeAnimation | AnimationOptions | 关闭动画 |
onWillDismiss | () => void | 关闭前回调 |
@CustomDialog提供了极高的灵活性,广告弹窗、协议确认、信息填写等场景都能优雅实现。
四、Popup:轻量级提示气泡
Popup(气泡提示)用于在组件附近显示补充信息或操作入口,特点是轻量、不打断当前操作流程。
使用 bindPopup 绑定气泡
通过bindPopup方法将气泡绑定到某个组件上,通过状态变量控制显示/隐藏。
typescript
@Entry @Component struct PopupExample { @State showPopup: boolean = false @Builder popupBuilder() { Row({ space: 8 }) { Image($r('app.media.icon')) .width(24) .height(24) Text('这是自定义气泡提示') .fontSize(14) } .padding(12) .backgroundColor(Color.White) .borderRadius(12) .shadow({ radius: 8, color: 0x33000000 }) } build() { Column({ space: 30 }) { // 方式一:使用默认样式(PopupOptions) Button('默认气泡') .onClick(() => { this.showPopup = !this.showPopup }) .bindPopup(this.showPopup, { message: '这是一条气泡提示消息', placementOnTop: false, primaryButton: { value: '知道了', action: () => { this.showPopup = false } }, onStateChange: (e) => { if (!e.isVisible) { this.showPopup = false } } }) // 方式二:使用自定义内容(CustomPopupOptions) Button('自定义气泡') .onClick(() => { this.showPopup = !this.showPopup }) .bindPopup(this.showPopup, { builder: this.popupBuilder, placement: Placement.Top, // 气泡在按钮上方 enableArrow: true, // 显示指向箭头 popupColor: Color.White, mask: { color: 0x22000000 }, // 半透明遮罩 onStateChange: (e) => { if (!e.isVisible) { this.showPopup = false } } }) } .width('100%') .padding(50) } }Popup 核心配置
| 参数 | 类型 | 说明 |
|---|---|---|
message | string | 气泡文字内容(PopupOptions) |
builder | CustomBuilder | 自定义气泡布局(CustomPopupOptions) |
placement | Placement | 显示位置,默认 Bottom |
enableArrow | boolean | 是否显示指向箭头 |
popupColor | Color | 气泡背景色 |
mask | { color } | 遮罩配置 |
showInSubWindow | boolean | 是否显示在子窗口 |
onStateChange | (e) => void | 显示/隐藏状态变化回调 |
Popup 非常适合功能引导、提示说明、快捷操作菜单等场景。
五、进阶:浮层(Overlay)与模态页面
对于更复杂的场景,HarmonyOS Next 还提供了 OverlayManager(浮层)、bindSheet(半模态页面)和 bindContentCover(全模态页面)。
OverlayManager:在页面之上添加独立浮层,与页面生命周期解耦,适合活动入口、悬浮按钮等场景。通过
UIContext获取 OverlayManager 实例进行添加/删除操作。bindSheet:在组件上绑定半模态页面,下方页面保持可见,适合设置项确认、选择器等场景。
bindContentCover:全屏模态覆盖,适合需要完整页面级交互的场景(如表单填写、详情查看)。
六、选型建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 简单确认/警告 | AlertDialog | 一行代码调用,快速实现 |
| 带输入框的表单弹窗 | @CustomDialog | 完全自定义,支持任意复杂布局 |
| 操作引导/补充信息 | Popup | 轻量级,不打断操作流程 |
| 活动入口/悬浮元素 | OverlayManager | 与页面解耦,生命周期独立 |
| 设置项确认、选择器 | bindSheet | 半模态,保留上下文可见 |
| 独立页面级交互 | bindContentCover | 全屏覆盖,类似新页面 |
七、注意事项
弹窗层级管理:多个弹窗叠加时,后打开的会覆盖在先打开的之上,注意控制弹窗的打开和关闭逻辑,避免堆叠混乱。
自定义弹窗的控制器生命周期:
CustomDialogController应在页面组件中声明,不要在build方法内创建,否则会导致重复实例化和状态丢失。autoCancel 与 onWillDismiss 配合:需要拦截关闭操作时,使用
onWillDismiss回调,并在回调中决定是否真正关闭。跨设备适配:弹窗的宽度、高度建议使用相对单位(vp、百分比),并结合
gridCount等属性适配不同屏幕尺寸。性能考虑:自定义弹窗的
builder内容应尽量精简,避免在弹窗内部执行耗时操作,确保打开和关闭动画流畅。