玩转HarmonyOS APP系统对话框
HarmonyOS APP开发中的系统对话框:AlertDialog、ActionSheet、TextPickerDialog、DatePickerDialog、自定义系统对话框
📌 核心要点:掌握 HarmonyOS 五大系统对话框的使用场景与配置技巧,从简单确认到复杂选择,用最少的代码实现最规范的交互
一、背景与动机
你一定遇到过这样的场景——用户点了"删除"按钮,你需要在弹出一个确认框,问一句"确定要删除吗?"。或者用户需要选择一个日期,你不想自己画日历组件,太麻烦了。再或者,你需要让用户从几个选项中选一个,但又不想跳转到新页面。
这些场景都有一个共同点:需要一个轻量的、模态的、系统级的交互界面。这就是系统对话框的用武之地。
系统对话框就像餐厅里的服务员——你不用自己跑厨房(跳转页面),也不用自己端盘子(自定义弹窗),只需要跟服务员说一声(调用API),他就会帮你把事情办好(弹出对话框),然后把结果告诉你(回调函数)。
HarmonyOS 提供了五种系统对话框:AlertDialog(警告对话框)、ActionSheet(列表选择)、TextPickerDialog(文本选择)、DatePickerDialog(日期选择),以及自定义对话框。它们各有各的绝活,今天我们一个一个来拆解。
二、核心原理
2.1 系统对话框分类与适用场景

flowchart TD
A[系统对话框] --> B{交互类型}
B --> C[确认/警告]
B --> D[列表选择]
B --> E[滚轮选择]
B --> F[自定义内容]
C --> G[AlertDialog<br/>警告对话框]
D --> H[ActionSheet<br/>列表选择弹窗]
E --> I{选择内容}
I --> J[TextPickerDialog<br/>文本滚轮选择]
I --> K[DatePickerDialog<br/>日期滚轮选择]
F --> L[CustomDialog<br/>自定义对话框]
G --> M[特点: 标题+内容+按钮<br/>场景: 删除确认/退出确认]
H --> N[特点: 选项列表+取消<br/>场景: 分享/操作菜单]
J --> O[特点: 滚轮+确认<br/>场景: 城市选择/分类选择]
K --> P[特点: 年月日滚轮<br/>场景: 日期选择/生日设置]
L --> Q[特点: 完全自定义<br/>场景: 复杂表单/加载动画]
classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef error fill:#F44336,stroke:#D32F2F,color:#fff
classDef info fill:#2196F3,stroke:#1976D2,color:#fff
classDef purple fill:#9C27B0,stroke:#7B1FA2,color:#fff
class A primary
class B,C,D,E,F info
class G,H,J,K,L warning
class M,N,O,P,Q purple
2.2 对话框生命周期
所有系统对话框都遵循统一的生命周期:创建 → 显示 → 用户交互 → 回调 → 销毁。它们是模态的——弹出后用户必须先处理对话框,才能继续操作应用。
2.3 关键API对比
| 对话框 | 调用方式 | 主要参数 | 返回结果 |
|---|---|---|---|
| AlertDialog | AlertDialog.show() |
title, message, buttons | 按钮索引 |
| ActionSheet | ActionSheet.show() |
title, message, buttons | 按钮索引 |
| TextPickerDialog | TextPickerDialog.show() |
range, selected | 选中值和索引 |
| DatePickerDialog | DatePickerDialog.show() |
selected, min, max | 年月日 |
| CustomDialog | @CustomDialog 装饰器 |
完全自定义 | 自定义回调 |
三、代码实战
3.1 AlertDialog 与 ActionSheet:确认与选择
AlertDialog 适合需要用户明确确认的场景(删除、退出、授权),ActionSheet 适合提供一组操作选项让用户选择。
// AlertDialogActionSheet.ets
// AlertDialog 与 ActionSheet 实战
@Entry
@Component
struct AlertDialogActionSheetDemo {
// 操作日志
@State actionLog: string = '等待操作...';
// 是否显示自定义对话框
@State showCustomDialog: boolean = false;
// AlertDialog:删除确认
private showDeleteConfirm(): void {
AlertDialog.show({
title: '确认删除',
message: '此操作将永久删除该文件,无法恢复。确定要继续吗?',
autoCancel: true, // 点击遮罩层是否关闭
alignment: DialogAlignment.Center,
buttons: [
{
text: '取消',
color: '#9E9E9E',
action: () => {
this.actionLog = '用户取消了删除操作';
}
},
{
text: '确定删除',
color: '#F44336',
action: () => {
this.actionLog = '文件已删除 ✓';
}
}
]
});
}
// AlertDialog:退出确认(三按钮)
private showExitConfirm(): void {
AlertDialog.show({
title: '保存更改',
message: '您有未保存的更改,是否在退出前保存?',
autoCancel: true,
buttons: [
{
text: '不保存',
color: '#F44336',
action: () => {
this.actionLog = '退出且不保存';
}
},
{
text: '取消',
color: '#9E9E9E',
action: () => {
this.actionLog = '取消退出,继续编辑';
}
},
{
text: '保存',
color: '#4CAF50',
action: () => {
this.actionLog = '保存更改并退出 ✓';
}
}
]
});
}
// ActionSheet:分享菜单
private showShareMenu(): void {
ActionSheet.show({
title: '分享到',
message: '选择分享方式',
autoCancel: true,
buttons: [
{ text: '微信', color: '#07C160' },
{ text: 'QQ', color: '#12B7F5' },
{ text: '微博', color: '#E6162D' },
{ text: '复制链接', color: '#E0E0E0' },
],
cancelButton: () => {
this.actionLog = '取消分享';
},
confirmationButton: () => {
// ActionSheet的确认回调
},
callback: (index: number) => {
const platforms = ['微信', 'QQ', '微博', '复制链接'];
this.actionLog = `分享到: ${platforms[index]} ✓`;
}
});
}
// ActionSheet:图片操作菜单
private showImageActions(): void {
ActionSheet.show({
title: '图片操作',
message: '选择操作方式',
autoCancel: true,
buttons: [
{ text: '查看大图', color: '#2196F3' },
{ text: '保存到相册', color: '#4CAF50' },
{ text: '设为头像', color: '#FF9800' },
{ text: '删除图片', color: '#F44336' },
],
cancelButton: () => {
this.actionLog = '取消操作';
},
callback: (index: number) => {
const actions = ['查看大图', '保存到相册', '设为头像', '删除图片'];
this.actionLog = `执行: ${actions[index]} ✓`;
}
});
}
build() {
Column() {
// 标题区域
Column() {
Text('系统对话框实战')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Text('AlertDialog & ActionSheet')
.fontSize(14)
.fontColor('#FFFFFFAA')
.margin({ top: 4 })
}
.width('100%')
.padding({ top: 56, left: 20, right: 20, bottom: 20 })
.linearGradient({
direction: GradientDirection.BottomRight,
colors: [['#1A1A2E', 0], ['#16213E', 1]]
})
// 操作日志
Column() {
Text('操作日志')
.fontSize(13)
.fontColor('#9E9E9E')
.margin({ bottom: 6 })
Text(this.actionLog)
.fontSize(15)
.fontColor('#4CAF50')
.fontWeight(FontWeight.Medium)
}
.width('100%')
.padding(14)
.margin({ left: 16, right: 16, top: 12 })
.borderRadius(10)
.backgroundColor('#1E1E2E')
.alignItems(HorizontalAlign.Start)
// AlertDialog 示例
Text('AlertDialog 警告对话框')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#E0E0E0')
.margin({ left: 20, top: 20, bottom: 8 })
Column({ space: 8 }) {
// 删除确认
Row() {
Text('🗑️')
.fontSize(18)
Text('删除确认(双按钮)')
.fontSize(14)
.fontColor('#E0E0E0')
.margin({ left: 8 })
Blank()
Text('→')
.fontSize(14)
.fontColor('#9E9E9E')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => this.showDeleteConfirm())
// 退出确认
Row() {
Text('💾')
.fontSize(18)
Text('保存退出(三按钮)')
.fontSize(14)
.fontColor('#E0E0E0')
.margin({ left: 8 })
Blank()
Text('→')
.fontSize(14)
.fontColor('#9E9E9E')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => this.showExitConfirm())
}
.padding({ left: 16, right: 16 })
// ActionSheet 示例
Text('ActionSheet 列表选择')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#E0E0E0')
.margin({ left: 20, top: 20, bottom: 8 })
Column({ space: 8 }) {
// 分享菜单
Row() {
Text('🔗')
.fontSize(18)
Text('分享菜单')
.fontSize(14)
.fontColor('#E0E0E0')
.margin({ left: 8 })
Blank()
Text('→')
.fontSize(14)
.fontColor('#9E9E9E')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => this.showShareMenu())
// 图片操作
Row() {
Text('🖼️')
.fontSize(18)
Text('图片操作菜单')
.fontSize(14)
.fontColor('#E0E0E0')
.margin({ left: 8 })
Blank()
Text('→')
.fontSize(14)
.fontColor('#9E9E9E')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => this.showImageActions())
}
.padding({ left: 16, right: 16 })
}
.width('100%')
.height('100%')
.backgroundColor('#121212')
}
}
3.2 TextPickerDialog 与 DatePickerDialog:滚轮选择
TextPickerDialog 适合从预定义列表中选择(城市、分类、学历),DatePickerDialog 适合选择日期(生日、预约时间)。
// PickerDialogs.ets
// TextPickerDialog 与 DatePickerDialog 实战
@Entry
@Component
struct PickerDialogsDemo {
// 选中的城市
@State selectedCity: string = '请选择';
// 选中的日期
@State selectedDate: string = '请选择';
// 选中的学历
@State selectedEducation: string = '请选择';
// 选中的时间段
@State selectedTimeSlot: string = '请选择';
// 城市数据
private cities: string[] = [
'北京', '上海', '广州', '深圳', '杭州', '成都', '武汉', '南京', '西安', '重庆'
];
// 学历数据
private educations: string[] = [
'高中及以下', '大专', '本科', '硕士', '博士', '其他'
];
// 时间段数据
private timeSlots: string[] = [
'09:00 - 10:00', '10:00 - 11:00', '11:00 - 12:00',
'14:00 - 15:00', '15:00 - 16:00', '16:00 - 17:00'
];
// 显示城市选择器
private showCityPicker(): void {
TextPickerDialog.show({
range: this.cities,
selected: 0,
onAccept: (value: string, index: number) => {
this.selectedCity = `${value} (索引: ${index})`;
},
onCancel: () => {
console.info('用户取消了城市选择');
}
});
}
// 显示学历选择器
private showEducationPicker(): void {
TextPickerDialog.show({
range: this.educations,
selected: 2, // 默认选中"本科"
onAccept: (value: string, index: number) => {
this.selectedEducation = value;
},
onCancel: () => {
console.info('用户取消了学历选择');
}
});
}
// 显示时间段选择器
private showTimeSlotPicker(): void {
TextPickerDialog.show({
range: this.timeSlots,
selected: 0,
onAccept: (value: string, index: number) => {
this.selectedTimeSlot = value;
},
onCancel: () => {
console.info('用户取消了时间段选择');
}
});
}
// 显示日期选择器
private showDatePicker(): void {
DatePickerDialog.show({
selected: new Date(), // 默认选中今天
min: new Date('1900-01-01'), // 最小日期
max: new Date('2030-12-31'), // 最大日期
lunar: false, // 是否显示农历
onAccept: (value: Date) => {
const year = value.getFullYear();
const month = String(value.getMonth() + 1).padStart(2, '0');
const day = String(value.getDate()).padStart(2, '0');
this.selectedDate = `${year}-${month}-${day}`;
},
onCancel: () => {
console.info('用户取消了日期选择');
}
});
}
build() {
Column() {
// 标题区域
Column() {
Text('滚轮选择器实战')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Text('TextPickerDialog & DatePickerDialog')
.fontSize(14)
.fontColor('#FFFFFFAA')
.margin({ top: 4 })
}
.width('100%')
.padding({ top: 56, left: 20, right: 20, bottom: 20 })
.linearGradient({
direction: GradientDirection.BottomRight,
colors: [['#1A1A2E', 0], ['#16213E', 1]]
})
// 选择结果展示
Column({ space: 10 }) {
// 城市选择结果
Row() {
Text('📍 城市')
.fontSize(14)
.fontColor('#9E9E9E')
.width(60)
Text(this.selectedCity)
.fontSize(15)
.fontColor(this.selectedCity === '请选择' ? '#9E9E9E' : '#4CAF50')
.fontWeight(FontWeight.Medium)
Blank()
Text('选择 >')
.fontSize(13)
.fontColor('#2196F3')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => this.showCityPicker())
// 学历选择结果
Row() {
Text('🎓 学历')
.fontSize(14)
.fontColor('#9E9E9E')
.width(60)
Text(this.selectedEducation)
.fontSize(15)
.fontColor(this.selectedEducation === '请选择' ? '#9E9E9E' : '#4CAF50')
.fontWeight(FontWeight.Medium)
Blank()
Text('选择 >')
.fontSize(13)
.fontColor('#2196F3')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => this.showEducationPicker())
// 时间段选择结果
Row() {
Text('🕐 时间')
.fontSize(14)
.fontColor('#9E9E9E')
.width(60)
Text(this.selectedTimeSlot)
.fontSize(15)
.fontColor(this.selectedTimeSlot === '请选择' ? '#9E9E9E' : '#4CAF50')
.fontWeight(FontWeight.Medium)
Blank()
Text('选择 >')
.fontSize(13)
.fontColor('#2196F3')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => this.showTimeSlotPicker())
// 日期选择结果
Row() {
Text('📅 日期')
.fontSize(14)
.fontColor('#9E9E9E')
.width(60)
Text(this.selectedDate)
.fontSize(15)
.fontColor(this.selectedDate === '请选择' ? '#9E9E9E' : '#4CAF50')
.fontWeight(FontWeight.Medium)
Blank()
Text('选择 >')
.fontSize(13)
.fontColor('#2196F3')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => this.showDatePicker())
}
.padding({ left: 16, right: 16, top: 16 })
// 选择器说明
Column() {
Text('💡 选择器使用提示')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#FF9800')
.margin({ bottom: 8 })
Text('• TextPickerDialog 适合预定义列表的单选场景')
.fontSize(12)
.fontColor('#BDBDBD')
Text('• DatePickerDialog 支持设置最小/最大日期范围')
.fontSize(12)
.fontColor('#BDBDBD')
.margin({ top: 4 })
Text('• selected 参数控制默认选中项的索引')
.fontSize(12)
.fontColor('#BDBDBD')
.margin({ top: 4 })
Text('• onAccept 回调返回选中的值和索引')
.fontSize(12)
.fontColor('#BDBDBD')
.margin({ top: 4 })
}
.width('100%')
.padding(14)
.margin({ left: 16, right: 16, top: 16 })
.borderRadius(10)
.backgroundColor('#1E1E2E')
.border({ width: 1, color: '#FF980033' })
}
.width('100%')
.height('100%')
.backgroundColor('#121212')
}
}
3.3 自定义系统对话框:CustomDialog
当系统对话框无法满足需求时(比如需要输入框、加载动画、复杂表单),就需要自定义对话框。
// CustomDialogDemo.ets
// 自定义系统对话框:CustomDialog 实战
// 自定义加载对话框
@CustomDialog
struct LoadingDialog {
// 对话框控制器
controller: CustomDialogController;
// 加载提示文字
@State loadingText: string = '加载中...';
// 进度(0-100)
@State progress: number = 0;
// 定时器
private timer: number = -1;
aboutToAppear(): void {
// 模拟加载进度
this.timer = setInterval(() => {
this.progress += Math.random() * 15;
if (this.progress >= 100) {
this.progress = 100;
clearInterval(this.timer);
this.loadingText = '加载完成!';
// 延迟关闭
setTimeout(() => {
this.controller.close();
}, 800);
} else {
this.loadingText = `加载中... ${Math.floor(this.progress)}%`;
}
}, 300);
}
aboutToDisappear(): void {
clearInterval(this.timer);
}
build() {
Column() {
// 加载动画
LoadingProgress()
.width(48)
.height(48)
.color('#4CAF50')
// 提示文字
Text(this.loadingText)
.fontSize(14)
.fontColor('#E0E0E0')
.margin({ top: 16 })
// 进度条
Progress({ value: this.progress, total: 100, type: ProgressType.Linear })
.width('80%')
.color('#4CAF50')
.backgroundColor('#FFFFFF22')
.margin({ top: 12 })
}
.padding(24)
.borderRadius(16)
.backgroundColor('#1E1E2E')
.alignItems(HorizontalAlign.Center)
}
}
// 自定义输入对话框
@CustomDialog
struct InputDialog {
controller: CustomDialogController;
// 输入内容
@State inputText: string = '';
// 标题
title: string = '请输入';
// 占位提示
placeholder: string = '请输入内容...';
// 确认回调
onConfirm: (text: string) => void = () => {};
build() {
Column() {
// 标题
Text(this.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
// 输入框
TextInput({ text: this.inputText, placeholder: this.placeholder })
.width('100%')
.height(44)
.fontSize(15)
.fontColor('#E0E0E0')
.placeholderColor('#9E9E9E')
.backgroundColor('#2A2A3E')
.borderRadius(10)
.padding({ left: 12, right: 12 })
.margin({ top: 16 })
.onChange((value: string) => {
this.inputText = value;
})
// 按钮行
Row({ space: 12 }) {
Text('取消')
.fontSize(15)
.fontColor('#9E9E9E')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(10)
.backgroundColor('#2A2A3E')
.onClick(() => {
this.controller.close();
})
Text('确定')
.fontSize(15)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.padding({ top: 10, bottom: 10 })
.borderRadius(10)
.backgroundColor('#4CAF50')
.onClick(() => {
this.onConfirm(this.inputText);
this.controller.close();
})
}
.width('100%')
.margin({ top: 20 })
}
.padding(20)
.borderRadius(16)
.backgroundColor('#1E1E2E')
}
}
// 自定义评分对话框
@CustomDialog
struct RatingDialog {
controller: CustomDialogController;
// 评分(1-5星)
@State rating: number = 0;
// 评价内容
@State comment: string = '';
// 确认回调
onSubmit: (rating: number, comment: string) => void = () => {};
build() {
Column() {
Text('请为本次服务评分')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
// 星级评分
Row({ space: 8 }) {
ForEach([1, 2, 3, 4, 5], (star: number) => {
Text(star <= this.rating ? '⭐' : '☆')
.fontSize(32)
.onClick(() => {
this.rating = star;
})
})
}
.margin({ top: 20 })
// 评分描述
Text(this.getRatingDescription())
.fontSize(13)
.fontColor(this.rating > 0 ? '#FF9800' : '#9E9E9E')
.margin({ top: 8 })
// 评价输入
TextArea({ text: this.comment, placeholder: '请输入您的评价(选填)...' })
.width('100%')
.height(80)
.fontSize(14)
.fontColor('#E0E0E0')
.placeholderColor('#9E9E9E')
.backgroundColor('#2A2A3E')
.borderRadius(10)
.padding(12)
.margin({ top: 16 })
.onChange((value: string) => {
this.comment = value;
})
// 提交按钮
Text('提交评价')
.fontSize(15)
.fontColor('#FFFFFF')
.width('100%')
.textAlign(TextAlign.Center)
.padding({ top: 12, bottom: 12 })
.borderRadius(10)
.backgroundColor(this.rating > 0 ? '#4CAF50' : '#4CAF5044')
.margin({ top: 16 })
.onClick(() => {
if (this.rating > 0) {
this.onSubmit(this.rating, this.comment);
this.controller.close();
}
})
}
.padding(20)
.borderRadius(16)
.backgroundColor('#1E1E2E')
}
// 获取评分描述
private getRatingDescription(): string {
const descriptions: Record<number, string> = {
0: '请点击星星评分',
1: '非常不满意 😞',
2: '不太满意 😐',
3: '一般般 🙂',
4: '比较满意 😊',
5: '非常满意 🤩',
};
return descriptions[this.rating] || '';
}
}
// 主页面
@Entry
@Component
struct CustomDialogDemoPage {
// 对话框控制器
private loadingDialogController: CustomDialogController = new CustomDialogController({
builder: LoadingDialog(),
autoCancel: false,
customStyle: true,
alignment: DialogAlignment.Center,
});
private inputDialogController: CustomDialogController = new CustomDialogController({
builder: InputDialog({
title: '修改昵称',
placeholder: '请输入新的昵称',
onConfirm: (text: string) => {
this.nickname = text;
}
}),
autoCancel: true,
customStyle: true,
alignment: DialogAlignment.Center,
});
private ratingDialogController: CustomDialogController = new CustomDialogController({
builder: RatingDialog({
onSubmit: (rating: number, comment: string) => {
this.userRating = rating;
this.userComment = comment;
}
}),
autoCancel: true,
customStyle: true,
alignment: DialogAlignment.Center,
});
// 页面状态
@State nickname: string = '鸿蒙开发者';
@State userRating: number = 0;
@State userComment: string = '';
build() {
Column() {
// 标题区域
Column() {
Text('自定义对话框实战')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Text('CustomDialog 完全自定义')
.fontSize(14)
.fontColor('#FFFFFFAA')
.margin({ top: 4 })
}
.width('100%')
.padding({ top: 56, left: 20, right: 20, bottom: 20 })
.linearGradient({
direction: GradientDirection.BottomRight,
colors: [['#1A1A2E', 0], ['#16213E', 1]]
})
// 当前状态
Column({ space: 8 }) {
Text(`昵称: ${this.nickname}`)
.fontSize(14)
.fontColor('#E0E0E0')
Text(`评分: ${this.userRating > 0 ? '⭐'.repeat(this.userRating) : '未评分'}`)
.fontSize(14)
.fontColor('#E0E0E0')
if (this.userComment) {
Text(`评价: ${this.userComment}`)
.fontSize(14)
.fontColor('#E0E0E0')
}
}
.width('100%')
.padding(14)
.margin({ left: 16, right: 16, top: 12 })
.borderRadius(10)
.backgroundColor('#1E1E2E')
.alignItems(HorizontalAlign.Start)
// 对话框触发按钮
Text('自定义对话框')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#E0E0E0')
.margin({ left: 20, top: 20, bottom: 8 })
Column({ space: 8 }) {
// 加载对话框
Row() {
Text('⏳')
.fontSize(18)
Text('加载进度对话框')
.fontSize(14)
.fontColor('#E0E0E0')
.margin({ left: 8 })
Blank()
Text('→')
.fontSize(14)
.fontColor('#9E9E9E')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => {
this.loadingDialogController.open();
})
// 输入对话框
Row() {
Text('✏️')
.fontSize(18)
Text('输入对话框(修改昵称)')
.fontSize(14)
.fontColor('#E0E0E0')
.margin({ left: 8 })
Blank()
Text('→')
.fontSize(14)
.fontColor('#9E9E9E')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => {
this.inputDialogController.open();
})
// 评分对话框
Row() {
Text('⭐')
.fontSize(18)
Text('评分对话框')
.fontSize(14)
.fontColor('#E0E0E0')
.margin({ left: 8 })
Blank()
Text('→')
.fontSize(14)
.fontColor('#9E9E9E')
}
.width('100%')
.padding(14)
.borderRadius(10)
.backgroundColor('#1E1E2E')
.onClick(() => {
this.ratingDialogController.open();
})
}
.padding({ left: 16, right: 16 })
// CustomDialog 要点
Column() {
Text('📌 CustomDialog 要点')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#9C27B0')
.margin({ bottom: 8 })
Text('• 使用 @CustomDialog 装饰器声明')
.fontSize(12)
.fontColor('#BDBDBD')
Text('• 通过 CustomDialogController 控制显隐')
.fontSize(12)
.fontColor('#BDBDBD')
.margin({ top: 4 })
Text('• customStyle: true 启用自定义样式')
.fontSize(12)
.fontColor('#BDBDBD')
.margin({ top: 4 })
Text('• autoCancel: false 禁止点击遮罩关闭')
.fontSize(12)
.fontColor('#BDBDBD')
.margin({ top: 4 })
Text('• 回调函数通过属性传递,而非事件')
.fontSize(12)
.fontColor('#BDBDBD')
.margin({ top: 4 })
}
.width('100%')
.padding(14)
.margin({ left: 16, right: 16, top: 16 })
.borderRadius(10)
.backgroundColor('#1E1E2E')
.border({ width: 1, color: '#9C27B033' })
}
.width('100%')
.height('100%')
.backgroundColor('#121212')
}
}
四、踩坑与注意事项
4.1 AlertDialog 按钮数量限制
AlertDialog 最多支持 3个按钮。如果你传入4个或更多按钮,系统只会显示前3个。如果需要更多选项,请使用 ActionSheet。
4.2 ActionSheet 的回调机制
ActionSheet 的回调有点特殊——callback 参数接收的是按钮索引,而 cancelButton 和 confirmationButton 是独立的回调。注意区分:
// callback: 点击选项按钮时触发
// cancelButton: 点击取消按钮时触发
ActionSheet.show({
buttons: [...],
callback: (index: number) => {
// 点击了第 index 个选项
},
cancelButton: () => {
// 点击了取消
}
});
4.3 TextPickerDialog 的 range 必须是非空数组
如果传入空数组,TextPickerDialog 会直接崩溃。务必在调用前检查数据源:
// ❌ 危险:空数组会导致崩溃
TextPickerDialog.show({ range: [], ... });
// ✅ 安全:先检查
if (this.cities.length > 0) {
TextPickerDialog.show({ range: this.cities, ... });
}
4.4 DatePickerDialog 的日期范围
min 和 max 参数必须合理设置。如果 selected 的日期不在 min 和 max 之间,对话框可能显示异常。建议始终显式设置日期范围。
4.5 CustomDialog 的生命周期
CustomDialog 的 aboutToAppear 和 aboutToDisappear 在对话框打开和关闭时触发。如果对话框中有定时器或异步操作,务必在 aboutToDisappear 中清理,否则会造成内存泄漏。
4.6 CustomDialogController 的限制
CustomDialogController 必须作为组件的成员变量声明,不能在方法内部动态创建。这意味着你不能根据运行时条件动态切换不同的对话框——你需要预先声明所有可能用到的控制器。
五、HarmonyOS 6适配
5.1 API变更
| 变更项 | HarmonyOS 5 | HarmonyOS 6 |
|---|---|---|
| AlertDialog | AlertDialog.show() |
新增 AlertDialog.showWithOptions() 支持更多配置 |
| ActionSheet | ActionSheet.show() |
新增 ActionSheet.showWithIcons() 支持图标 |
| TextPickerDialog | 单列选择 | 新增多列联动选择 |
| DatePickerDialog | 年月日 | 新增 TimePickerDialog 时间选择 |
| CustomDialog | @CustomDialog |
新增 @PromptAction 声明式弹窗 |
5.2 迁移指南
// HarmonyOS 5 写法
TextPickerDialog.show({
range: this.cities,
selected: 0,
onAccept: (value: string, index: number) => { ... },
onCancel: () => { ... }
});
// HarmonyOS 6 写法(多列联动)
TextPickerDialog.show({
range: [this.provinces, this.citiesByProvince], // 多列数据
selected: [0, 0], // 每列的默认选中
onAccept: (values: string[], indices: number[]) => { ... },
onCancel: () => { ... }
});
5.3 新增 TimePickerDialog
HarmonyOS 6 新增了 TimePickerDialog,专门用于时间选择,弥补了之前只能选日期不能选时间的短板。
六、总结
mindmap
root((系统对话框))
AlertDialog
确认/警告场景
最多3个按钮
autoCancel控制遮罩关闭
删除确认/退出确认
ActionSheet
列表选择场景
底部弹出
callback获取索引
分享菜单/操作菜单
TextPickerDialog
滚轮文本选择
range数据源
selected默认选中
城市选择/分类选择
DatePickerDialog
日期滚轮选择
min/max范围限制
selected默认日期
生日选择/预约日期
CustomDialog
完全自定义
@CustomDialog装饰器
CustomDialogController
customStyle: true
回调通过属性传递
踩坑要点
按钮数量限制
空数组崩溃
日期范围校验
定时器清理
Controller不能动态创建
HarmonyOS 6
多列联动选择
TimePickerDialog
showWithIcons
@PromptAction
classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef error fill:#F44336,stroke:#D32F2F,color:#fff
classDef info fill:#2196F3,stroke:#1976D2,color:#fff
classDef purple fill:#9C27B0,stroke:#7B1FA2,color:#fff
| 对话框 | 适用场景 | 核心参数 | 注意事项 |
|---|---|---|---|
| AlertDialog | 确认/警告 | title, message, buttons | 最多3个按钮 |
| ActionSheet | 列表选择 | title, buttons, callback | 区分callback和cancelButton |
| TextPickerDialog | 文本滚轮 | range, selected, onAccept | range不能为空 |
| DatePickerDialog | 日期选择 | selected, min, max | 设置合理的日期范围 |
| CustomDialog | 完全自定义 | @CustomDialog, Controller | 清理定时器,Controller不能动态创建 |
系统对话框是应用交互的"第一道门面"。用对了,用户体验丝滑流畅;用错了,用户可能一脸懵逼。记住一个原则:能用系统对话框解决的,就不要自己造轮子。系统对话框经过充分测试和适配,在所有设备上都能保证一致的体验。
- 点赞
- 收藏
- 关注作者
评论(0)