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:添加弹窗队列
实现多个弹窗依次显示。
下一课预告
第22课我们将学习图片处理与资源管理,包括:
- 图片资源类型
- 图片组件配置
- 图片处理能力
- 资源管理器使用
项目开源地址
https://gitcode.com/daleishen/gujinzhijian
