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

HarmonyOS APP<<古今职鉴定>>开源教程第21篇:弹窗与对话框设计

本篇学习各种弹窗组件,实现年俗弹窗与倒计时关闭

图:弹窗与对话框设计 的关键流程与实现要点。

学习目标

完成本篇后,你将能够:

  • ✅ 使用系统弹窗组件
  • ✅ 创建自定义弹窗
  • ✅ 实现弹窗动画效果
  • ✅ 开发年俗弹窗功能

预计学习时间

约 90 分钟


实战一:系统弹窗组件

第一步:AlertDialog 警告弹窗

@Entry @Component struct Lesson21Page { showAlertDialog() { AlertDialog.show({ title: '提示', message: '确定要退出吗?', autoCancel: true, alignment: DialogAlignment.Center, primaryButton: { value: '取消', action: () => { console.log('点击取消'); } }, secondaryButton: { value: '确定', fontColor: '#c41e3a', action: () => { console.log('点击确定'); } } }); } build() { Column() { Button('显示警告弹窗') .onClick(() => { this.showAlertDialog(); }) } } }

第二步:ActionSheet 操作面板

showActionSheet() { ActionSheet.show({ title: '选择朝代', message: '请选择要查看的朝代', autoCancel: true, sheets: [ { title: '秦朝', action: () => console.log('秦') }, { title: '汉朝', action: () => console.log('汉') }, { title: '唐朝', action: () => console.log('唐') }, { title: '宋朝', action: () => console.log('宋') }, { title: '明朝', action: () => console.log('明') }, { title: '清朝', action: () => console.log('清') } ] }); }

第三步:DatePickerDialog 日期选择

showDatePicker() { DatePickerDialog.show({ start: new Date('2020-1-1'), end: new Date('2030-12-31'), selected: new Date(), lunar: true, // 显示农历 onAccept: (value: DatePickerResult) => { console.log(`选择日期: ${value.year}-${value.month}-${value.day}`); } }); }

第四步:TextPickerDialog 文本选择

showTextPicker() { TextPickerDialog.show({ range: ['秦朝', '汉朝', '唐朝', '宋朝', '明朝', '清朝'], selected: 0, onAccept: (value: TextPickerResult) => { console.log(`选择: ${value.value}`); } }); }

实战二:自定义弹窗

第一步:创建自定义弹窗组件

@CustomDialog struct CustomAlertDialog { controller: CustomDialogController; title: string = ''; message: string = ''; onConfirm: () => void = () => {}; build() { Column({ space: 16 }) { Text(this.title) .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor('#1e293b') Text(this.message) .fontSize(14) .fontColor('#64748b') .textAlign(TextAlign.Center) Row({ space: 12 }) { Button('取消') .layoutWeight(1) .backgroundColor('#f0f0f0') .fontColor('#1e293b') .onClick(() => { this.controller.close(); }) Button('确定') .layoutWeight(1) .backgroundColor('#c41e3a') .onClick(() => { this.onConfirm(); this.controller.close(); }) } .width('100%') } .width('80%') .padding(24) .backgroundColor(Color.White) .borderRadius(16) } }

第二步:使用自定义弹窗

@Entry @Component struct Lesson21Page { private dialogController: CustomDialogController = new CustomDialogController({ builder: CustomAlertDialog({ title: '自定义弹窗', message: '这是一个自定义弹窗示例', onConfirm: () => { console.log('确认'); } }), autoCancel: true, alignment: DialogAlignment.Center, customStyle: true }); build() { Column() { Button('显示自定义弹窗') .onClick(() => { this.dialogController.open(); }) } } }

第三步:弹窗配置选项

选项说明
autoCancel点击遮罩是否关闭
alignment弹窗位置
customStyle使用自定义样式
offset位置偏移
gridCount弹窗宽度(栅格数)

实战三:弹窗动画效果

第一步:入场动画

@CustomDialog struct AnimatedDialog { controller: CustomDialogController; @State scale: number = 0.8; @State opacity: number = 0; aboutToAppear() { animateTo({ duration: 300, curve: Curve.EaseOut }, () => { this.scale = 1; this.opacity = 1; }); } build() { Column() { Text('动画弹窗') .fontSize(18) .fontColor('#1e293b') } .width('80%') .padding(24) .backgroundColor(Color.White) .borderRadius(16) .scale({ x: this.scale, y: this.scale }) .opacity(this.opacity) } }

第二步:关闭动画

@CustomDialog struct AnimatedDialog { controller: CustomDialogController; @State scale: number = 1; @State opacity: number = 1; closeWithAnimation() { animateTo({ duration: 200, curve: Curve.EaseIn, onFinish: () => { this.controller.close(); } }, () => { this.scale = 0.8; this.opacity = 0; }); } build() { Column({ space: 16 }) { Text('动画弹窗') .fontSize(18) .fontColor('#1e293b') Button('关闭') .onClick(() => { this.closeWithAnimation(); }) } .width('80%') .padding(24) .backgroundColor(Color.White) .borderRadius(16) .scale({ x: this.scale, y: this.scale }) .opacity(this.opacity) } }

实战四:年俗弹窗实现

第一步:创建年俗弹窗组件

interface YearCustomInfo { name: string; description: string; lunarDate: string; } @CustomDialog struct YearCustomDialog { controller: CustomDialogController; customInfo: YearCustomInfo = { name: '', description: '', lunarDate: '' }; @State countdown: number = 3; @State scale: number = 0.8; @State opacity: number = 0; private timerId: number = -1; aboutToAppear() { // 入场动画 animateTo({ duration: 300, curve: Curve.EaseOut }, () => { this.scale = 1; this.opacity = 1; }); // 倒计时 this.startCountdown(); } aboutToDisappear() { if (this.timerId !== -1) { clearInterval(this.timerId); } } startCountdown() { this.timerId = setInterval(() => { this.countdown--; if (this.countdown <= 0) { clearInterval(this.timerId); this.closeWithAnimation(); } }, 1000); } closeWithAnimation() { animateTo({ duration: 200, curve: Curve.EaseIn, onFinish: () => { this.controller.close(); } }, () => { this.scale = 0.8; this.opacity = 0; }); } build() { Column({ space: 16 }) { // 农历日期 Text(this.customInfo.lunarDate) .fontSize(14) .fontColor('#64748b') // 年俗名称 Text(this.customInfo.name) .fontSize(28) .fontWeight(FontWeight.Bold) .fontColor('#c41e3a') // 描述 Text(this.customInfo.description) .fontSize(14) .fontColor('#64748b') .textAlign(TextAlign.Center) // 倒计时 Text(`${this.countdown}秒后自动关闭`) .fontSize(12) .fontColor('#9ca3af') .margin({ top: 8 }) // 关闭按钮 Button('知道了') .width('100%') .backgroundColor('#c41e3a') .margin({ top: 8 }) .onClick(() => { if (this.timerId !== -1) { clearInterval(this.timerId); } this.closeWithAnimation(); }) } .width('80%') .padding(24) .backgroundColor(Color.White) .borderRadius(16) .scale({ x: this.scale, y: this.scale }) .opacity(this.opacity) } }

第二步:在页面中使用

@Entry @Component struct Lesson21Page { @State showYearCustom: boolean = false; private yearCustomDialogController: CustomDialogController = new CustomDialogController({ builder: YearCustomDialog({ customInfo: { name: '贴春联', description: '贴对联、门神,迎接新年', lunarDate: '腊月廿九' } }), autoCancel: false, alignment: DialogAlignment.Center, customStyle: true }); aboutToAppear() { // 检查是否需要显示年俗弹窗 this.checkYearCustom(); } checkYearCustom() { // 模拟:如果是年俗日期,显示弹窗 setTimeout(() => { this.yearCustomDialogController.open(); }, 1000); } build() { Column({ space: 20 }) { // 头部 Row() { Text('弹窗与对话框') .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor('#1e293b') } .width('100%') .height(56) .padding({ left: 16, right: 16 }) .backgroundColor(Color.White) // 按钮列表 Column({ space: 12 }) { Button('警告弹窗') .width('100%') .onClick(() => { this.showAlertDialog(); }) Button('操作面板') .width('100%') .onClick(() => { this.showActionSheet(); }) Button('日期选择') .width('100%') .onClick(() => { this.showDatePicker(); }) Button('年俗弹窗') .width('100%') .backgroundColor('#c41e3a') .onClick(() => { this.yearCustomDialogController.open(); }) } .width('100%') .padding(16) } .width('100%') .height('100%') .backgroundColor('#f8f6f5') } showAlertDialog() { AlertDialog.show({ title: '提示', message: '这是一个警告弹窗', primaryButton: { value: '确定', action: () => {} } }); } showActionSheet() { ActionSheet.show({ title: '选择操作', sheets: [ { title: '选项一', action: () => {} }, { title: '选项二', action: () => {} }, { title: '选项三', action: () => {} } ] }); } showDatePicker() { DatePickerDialog.show({ selected: new Date(), lunar: true, onAccept: () => {} }); } } @Builder export function Lesson21PageBuilder() { Lesson21Page() }

第三步:运行验证

hvigorw assembleHap --no-daemon

本课小结

核心知识点

知识点说明
AlertDialog警告弹窗
ActionSheet操作面板
DatePickerDialog日期选择
@CustomDialog自定义弹窗装饰器
CustomDialogController弹窗控制器

弹窗最佳实践

  1. 合理使用系统弹窗
  2. 自定义弹窗注意动画
  3. 及时清理定时器
  4. 处理遮罩点击

课后练习

练习1:实现底部弹出面板

创建从底部滑出的弹窗。

练习2:添加弹窗队列

实现多个弹窗依次显示。


下一课预告

第22课我们将学习图片处理与资源管理,包括:

  • 图片资源类型
  • 图片组件配置
  • 图片处理能力
  • 资源管理器使用

项目开源地址

https://gitcode.com/daleishen/gujinzhijian

http://www.jsqmd.com/news/865528/

相关文章:

  • taotoken的token消耗明细在控制台中的可视化体验
  • 如何快速掌握GetQzonehistory:QQ空间备份的完整教程
  • HarmonyOS APP<<古今职鉴定>>开源教程第22篇:图片处理与资源管理
  • 2026西宁婚纱摄影推荐TOP5!这几家口碑好到爆! - charlieruizvin
  • 2026年北京被动房全案服务商选型指南|利坚美EPC总承包如何破局权责推诿陷阱 - 企业名录优选推荐
  • 2026全球名义雇主EOR服务商优选,泰国海外人力资源服务商推荐 - 品牌2025
  • 国内核心空港空运报关服务商技术能力实测对比 - 奔跑123
  • 过度设计是程序员的“职业病”,如何克制?
  • 推挽变压器深度解析:隔离电源设计中的选型准则与工程验证
  • 拍了一堆没修图的照片发不出去?这个私有相册让我终于不用再“表演”生活了
  • 从化区做美妆日化老板娘自己管账多年账务很乱换哪家代账公司能规范?|3个风险点+梳理路径全解 - 欢欢在创业
  • 简单比复杂更难:技术人如何修炼“简化”的能力?
  • 【信息科学与工程学】计算机科学与自动化——第十五篇 云计算 第三系列 亿级并发的算法
  • 成都搬家公司哪家靠谱?2026 口碑 TOP5 新鲜出炉 - 资讯速览
  • 长沙学校毕业典礼大型活动拍摄:定格现场温度 留存记忆 - 奔跑123
  • Forza Painter完整指南:如何将任何图片转换为《极限竞速》专业车辆涂装
  • 如何用3步将B站缓存视频变回可播放的MP4文件?
  • GitHub Copilot多模型集成深度解析:开发者如何根据场景选择最优AI助手?
  • 出海业务网络怎么选:专线还是 SD-WAN?
  • 商用智能咖啡机采购指南:智能咖啡机怎么选?多维度对比哪家技术强 - 品牌2025
  • 使用 Codex 的一次危险提醒
  • 亨得利夜间停走故障维修深度解析:2026年7城实探,从“睡一觉就不走了”到满血复活全记录 - 亨得利腕表维修中心
  • UWB汽车雷达哨兵模式全解 | 全网独家拆解,功耗+隐私双突破篇 引入低功耗唤醒机制+隐私脱敏算法,破解传统哨兵续航短、隐私泄露痛点,助力车载安防量产落地
  • 2026年北京被动房全案服务商选型指南:从设计咨询到PHI认证的完整对标 - 企业名录优选推荐
  • 2026年北京被动房全案服务商选型指南|从设计到认证的零碳建筑一站式方案 - 企业名录优选推荐
  • 问了4个AI模型,推荐门店稽查公司竟没有一家提到小零科技?问题出在哪
  • 2026 上海卡地亚戒指回收避坑攻略:五大平台专业度与套路揭秘 - 李宏哲1
  • 2026年昆明财税行业发展现状 代理记账服务多维信息梳理 - 兔兔不是荼荼
  • Neo4j 知识图谱:实体建模+Cypher查询+LangChain接入
  • Reliance Electric 805401-5R电源模块接口架