当前位置: 首页 > news >正文

OpenHarmony CustomDialog 自定义弹窗实战开发

一、前言

前文使用的AlertDialog样式固定,仅能配置标题、文本、按钮,无法满足头像选择、表单弹窗、确认弹窗、底部弹窗等个性化界面需求。OpenHarmony 提供CustomDialog自定义弹窗组件,基于 ArkUI 自由布局,支持自定义样式、交互、尺寸、动画,是项目中高频使用的进阶组件。

本文讲解自定义弹窗定义、调用传参、弹窗样式、关闭逻辑、嵌套交互,搭配多个可直接运行的案例,并结合全局状态完成综合实战。

二、核心基础说明

  1. 定义规则:使用@CustomDialog装饰器声明自定义弹窗,区别于普通页面组件@Component
  2. 弹窗参数:通过@DialogParam接收外部传入参数、回调函数,实现父子通信。
  3. 关闭弹窗:组件内置controller.close()方法,主动关闭弹窗。
  4. 弹窗控制器CustomDialogController用于创建、唤起、控制弹窗。

三、基础用法:简单提示弹窗

3.1 基础自定义弹窗(纯提示 + 双按钮)

ets

// 1. 定义自定义弹窗 @CustomDialog struct SimpleDialog { // 弹窗控制器,内置方法:close() controller: CustomDialogController // 接收外部回调 @DialogParam confirmCallback: () => void @DialogParam cancelCallback: () => void build() { Column({ space: 20 }) { // 弹窗标题 Text("操作提示") .fontSize(20) .fontWeight(FontWeight.Bold) // 内容 Text("确定执行当前操作吗?").fontSize(16) // 按钮区域 Row({ space: 30 }) { Button("取消") .width(100) .backgroundColor("#999") .onClick(() => { this.controller.close() // 关闭弹窗 this.cancelCallback() // 执行取消回调 }) Button("确定") .width(100) .backgroundColor("#007DFF") .onClick(() => { this.controller.close() this.confirmCallback() // 执行确定回调 }) } } .width(300) .padding(20) .backgroundColor(Color.White) .borderRadius(12) } } // 2. 页面入口,调用弹窗 @Entry @Component struct DialogIndex { // 创建弹窗控制器 dialogController: CustomDialogController = new CustomDialogController({ builder: SimpleDialog({ confirmCallback: () => { console.log("点击了确定按钮") }, cancelCallback: () => { console.log("点击了取消按钮") } }), // 弹窗配置:点击遮罩是否关闭弹窗 autoCancel: true }) build() { Column() { Button("打开自定义提示弹窗") .width(220) .onClick(() => { this.dialogController.open() // 唤起弹窗 }) } .width("100%") .height("100%") .justifyContent(FlexAlign.Center) } }

3.2 配置项说明

CustomDialogController中可配置常用属性:

  • autoCancel:布尔值,点击弹窗外部遮罩层是否关闭弹窗,默认true
  • alignment:弹窗对齐方式(居中、底部、顶部、左侧、右侧)
  • offset:弹窗偏移距离
  • maskColor:遮罩层颜色

四、进阶案例 1:带输入框的表单弹窗

适用于弹窗内输入内容、评论、备注、修改昵称等场景,实现弹窗内数据收集。

ets

@CustomDialog struct InputDialog { controller: CustomDialogController // 回传给父页面的输入内容 @DialogParam submit: (content: string) => void @State inputText: string = "" build() { Column({ space: 18 }) { Text("请输入备注信息").fontSize(18).fontWeight(FontWeight.Bold) // 弹窗内输入框 TextInput({ placeholder: "在此输入内容", text: this.inputText }) .width("100%") .height(45) .onChange(val => this.inputText = val) Row({ space: 20 }) { Button("关闭") .layoutWeight(1) .backgroundColor("#EEEEEE") .fontColor("#333") .onClick(() => this.controller.close()) Button("提交") .layoutWeight(1) .backgroundColor("#007DFF") .onClick(() => { this.submit(this.inputText) this.controller.close() }) } } .width(320) .padding(20) .backgroundColor(Color.White) .borderRadius(10) } } @Entry @Component struct InputDialogPage { @State result: string = "" inputDialog: CustomDialogController = new CustomDialogController({ builder: InputDialog({ submit: (val: string) => { this.result = "弹窗输入内容:" + val } }), autoCancel: false // 禁止点击遮罩关闭 }) build() { Column({ space: 20 }) { Button("打开输入弹窗") .width(220) .onClick(() => this.inputDialog.open()) Text(this.result).fontSize(18) } .width("100%") .height("100%") .justifyContent(FlexAlign.Center) .padding(20) } }

五、进阶案例 2:底部弹窗(底部弹出菜单)

通过alignment实现从页面底部弹出的菜单弹窗,常用于分享、操作菜单。

ets

@CustomDialog struct BottomMenuDialog { controller: CustomDialogController @DialogParam menuClick: (index: number) => void build() { Column() { List({ space: 1 }) { ListItem() { Text("分享好友") .width("100%") .height(50) .textAlign(TextAlign.Center) .fontSize(17) .onClick(() => { this.menuClick(1) this.controller.close() }) } ListItem() { Text("收藏内容") .width("100%") .height(50) .textAlign(TextAlign.Center) .fontSize(17) .onClick(() => { this.menuClick(2) this.controller.close() }) } ListItem() { Text("举报") .width("100%") .height(50) .textAlign(TextAlign.Center) .fontSize(17) .onClick(() => { this.menuClick(3) this.controller.close() }) } } .width("100%") // 取消按钮 Button("取消") .width("100%") .height(50) .margin({ top: 10 }) .backgroundColor(Color.White) .onClick(() => this.controller.close()) } .width("100%") .backgroundColor("#F5F5F5") } } @Entry @Component struct BottomDialogPage { @State tip: string = "" bottomDialog: CustomDialogController = new CustomDialogController({ builder: BottomMenuDialog({ menuClick: (idx: number) => { switch (idx) { case 1: this.tip = "已选择:分享好友" break case 2: this.tip = "已选择:收藏内容" break case 3: this.tip = "已选择:举报" break } } }), alignment: DialogAlignment.Bottom, // 弹窗底部对齐 autoCancel: true }) build() { Column({ space: 20 }) { Button("打开底部菜单弹窗") .width(220) .onClick(() => this.bottomDialog.open()) Text(this.tip).fontSize(18) } .width("100%") .height("100%") .justifyContent(FlexAlign.Center) } }

六、综合实战:弹窗修改全局用户名(联动 AppStorage)

结合之前的全局状态AppStorage,实现弹窗修改用户名 → 全局数据同步刷新完整业务。

ets

// 自定义修改昵称弹窗 @CustomDialog struct EditNameDialog { controller: CustomDialogController // 接收父页面传入的原始名称 @DialogParam oldName: string @DialogParam saveAction: (newName: string) => void @State tempName: string = "" aboutToAppear() { // 弹窗打开时赋值初始值 this.tempName = this.oldName } build() { Column({ space: 15 }) { Text("修改用户名").fontSize(20).fontWeight(FontWeight.Bold) TextInput({ text: this.tempName }) .width("100%") .height(45) .onChange(val => this.tempName = val) Row({ space: 20 }) { Button("取消") .layoutWeight(1) .onClick(() => this.controller.close()) Button("保存") .layoutWeight(1) .backgroundColor("#007DFF") .onClick(() => { this.saveAction(this.tempName) this.controller.close() }) } } .width(300) .padding(20) .backgroundColor(Color.White) .borderRadius(12) } } @Entry @Component struct UserInfoPage { // 双向绑定全局用户名 @StorageLink("userName") userName: string editDialog: CustomDialogController = new CustomDialogController({ builder: EditNameDialog({ oldName: this.userName, saveAction: (newName: string) => { // 修改全局数据,所有绑定页面自动刷新 this.userName = newName } }) }) build() { Column({ space: 25 }) { Text("当前用户名:" + this.userName).fontSize(20) Button("点击修改用户名") .width(220) .onClick(() => this.editDialog.open()) } .width("100%") .height("100%") .justifyContent(FlexAlign.Center) .padding(20) } }

七、开发要点、踩坑与最佳实践

7.1 核心使用规则

  1. 装饰器区分:
    • @CustomDialog:仅用于定义弹窗,不能作为独立页面
    • @Component:普通组件 / 页面
  2. 弹窗内部必须依赖controller.close()关闭,无法直接路由关闭。
  3. 外部传参、回调统一使用@DialogParam,不建议直接使用@Prop

7.2 常用配置技巧

  • 居中弹窗(默认):无需设置alignment
  • 底部弹窗:alignment: DialogAlignment.Bottom
  • 禁止点击遮罩关闭:autoCancel: false(表单、重要确认弹窗推荐开启)
  • 自定义遮罩:maskColor: "rgba(0,0,0,0.6)"

7.3 常见问题解决

  1. 弹窗多次创建报错控制器建议定义在组件顶层,不要在onClick内部重复new
  2. 弹窗输入框数据不回显在弹窗aboutToAppear生命周期中接收初始值,不要直接赋值。
  3. 弹窗尺寸适配固定宽高适配小弹窗,百分比宽高适配底部全屏菜单。

7.4 项目规范

  1. 按业务拆分弹窗:提示弹窗、表单弹窗、菜单弹窗,单独文件管理。
  2. 重要操作弹窗(退出账号、删除数据)设置autoCancel: false,防止误触。
  3. 弹窗样式统一:圆角、内边距、配色保持项目 UI 风格一致。
http://www.cnnetsun.cn/news/3632718.html

相关文章:

  • AI编程助手Token成本控制:从原理到实践的优化策略
  • 软考 系统架构设计师历年真题集萃(303)
  • 地图前端性能复盘:大量 Marker 与轨迹线的渲染优化实战
  • ViT 训练实战:视觉 Transformer 的收敛比 CNN 慢,但上限更高
  • Kali Linux安装全攻略:从入门到精通
  • AI网络监测系统:LSTM预测偶发中断实战
  • CDCM6208V1F时钟发生器:时序、功耗与高速系统设计实战
  • 法庭沟通策略_court-communication-strategy
  • 多模态AI加速药物研发:DrugCLIP技术解析与应用
  • 编程语言接入_add-lang
  • 区块链 + AI 项目半年复盘:技术可行不等于商业可行
  • AI学术写作工具PaperZZ:从选题到成文的全流程优化
  • OMAP-L138外设接口深度解析:USB、EMAC与LCD控制器寄存器配置与硬件设计
  • 本地文本向量化实践:sentence-transformers优化指南
  • 终极VLC美化指南:5款VeLoCity皮肤包快速安装与个性化设置方法
  • 3步构建股票智能分析自动化部署系统
  • 如何搭建个人AI本地知识库?
  • Linux文件链接原理与应用场景详解
  • 智能证件照API:一站式图像处理与合规检测解决方案
  • 寻找中国靠谱谷歌SEO服务商?找专注大鱼营销的团队更易获客。
  • 扣子数据库事务写入失败全链路排查(从连接池到WAL日志的终极诊断手册)
  • 豆包转 Word 工具推荐?办公党首选 AI 导出鸭,一键无损导出高效省心
  • 多模态3D建模在工业质检中的实践与挑战
  • Spring Boot与Quartz构建分布式定时任务系统实战指南
  • Linux生产环境硬盘挂载:用UUID彻底解决盘符漂移问题
  • Wayfinder Router:构建混合AI架构的智能路由解决方案
  • 3分钟免费汉化Figma:设计师必备的中文界面插件终极指南
  • 百度网盘直链解析:3个技巧告别限速的完整方案
  • PPTTimer:Windows平台智能演讲计时器终极指南,免费实现专业级时间掌控
  • 大语言模型群体学习机制解析:从原理到应用实践