HarmonyOS7 弹窗全家桶:AlertDialog、CustomDialog、ActionSheet 一个都不落下

文章目录

      • 前言
      • 弹窗类型对比
      • AlertDialog:最简单的提示弹窗
      • ActionSheet:底部选项弹窗
      • CustomDialog:自定义弹窗的完整实现
      • 弹窗动画
      • 弹窗嵌套处理
      • 写在最后

前言

你敢信?我之前项目里所有弹窗都用AlertDialog搞定,产品经理说"这个弹窗要加个输入框",我直接傻了——AlertDialog 根本放不了自定义内容啊。后来才知道 CustomDialog 才是正解,ActionSheet 又是另一种玩法。

三种弹窗各有各的场,用错了就是给自己挖坑。

今天把这三种弹窗全讲一遍,看完你就知道什么时候该用谁。

弹窗类型对比

先上对比表,一目了然:

维度AlertDialogActionSheetCustomDialog
用途简单提示/确认底部多选项完全自定义内容
位置屏幕中央屏幕底部自由设定
自定义内容不支持不支持完全自定义
按钮数量1-3 个多个选项随你放
调用方式全局方法全局方法Controller 控制
复杂度最低较高

划重点:简单的用 AlertDialog,选操作用 ActionSheet,啥都要自定义就用 CustomDialog。

AlertDialog:最简单的提示弹窗

AlertDialog 适合"你确定要删除吗?"这种场景,一行代码搞定:

AlertDialog.show({title:'提示',message:'确定要退出登录吗?',primaryButton:{value:'取消',action:()=>{console.info('用户点了取消');}},secondaryButton:{value:'确定',action:()=>{console.info('用户点了确定,执行退出');}}});

关键代码讲解:

  • primaryButton是主按钮(通常放取消),secondaryButton是次按钮(通常放确认)
  • 只需要单个按钮的话,只写primaryButton就行
  • action回调里处理点击逻辑,弹窗点击按钮后自动关闭

三个按钮的写法:

AlertDialog.show({title:'文件已修改',message:'是否保存更改?',primaryButton:{value:'不保存',action:()=>{}},secondaryButton:{value:'取消',action:()=>{}},thirdButton:{value:'保存',action:()=>{this.saveFile();}}});

  • thirdButton是第三个按钮,最多支持三个

ActionSheet:底部选项弹窗

ActionSheet 从底部弹出,适合"选择图片来源"这种多选项场景:

ActionSheet.show({title:'选择图片来源',message:'请选择照片获取方式',sheets:[{title:'拍照',action:()=>{this.openCamera();}},{title:'从相册选择',action:()=>{this.openGallery();}},{title:'从文件选择',action:()=>{this.openFileManager();}}],confirm:{value:'取消',action:()=>{}}});

关键代码讲解:

  • sheets是选项数组,每个选项有titleaction
  • confirm是底部的取消按钮,点击关闭弹窗
  • 选项会按数组顺序从上到下排列,通常把破坏性操作(如删除)放最后并标红

实际项目里经常需要区分"安全操作"和"危险操作":

ActionSheet.show({title:'操作',sheets:[{title:'编辑',action:()=>{}},{title:'分享',action:()=>{}},{title:'删除',action:()=>{this.deleteItem();}}],confirm:{value:'取消',action:()=>{}}});
  • "删除"这种危险操作,UI 上通常用红色文字区分,ActionSheet 本身不提供颜色配置,这个得靠 CustomDialog 实现

CustomDialog:自定义弹窗的完整实现

AlertDialog 和 ActionSheet 都只能放文字按钮,CustomDialog 才是真正的大杀器——你想放啥放啥。

完整代码,逐行讲解:

@CustomDialogstruct LoginDialog{controller?:CustomDialogController;@Stateusername:string='';@Statepassword:string='';onConfirm:(username:string,password:string)=>void=()=>{};build(){Column({space:20}){Text('用户登录').fontSize(22).fontWeight(FontWeight.Bold)TextInput({placeholder:'请输入用户名'}).width('100%').onChange((value:string)=>{this.username=value;})TextInput({placeholder:'请输入密码'}).width('100%').type(InputType.Password).onChange((value:string)=>{this.password=value;})Row({space:16}){Button('取消').width('45%').backgroundColor('#E0E0E0').fontColor('#333').onClick(()=>{this.controller?.close();})Button('登录').width('45%').onClick(()=>{this.controller?.close();this.onConfirm(this.username,this.password);})}.width('100%').justifyContent(FlexAlign.SpaceBetween)}.padding(24).borderRadius(16).backgroundColor(Color.White)}}

逐行讲解:

  • @CustomDialog装饰器标记这是一个自定义弹窗组件
  • controller?: CustomDialogController—— 控制器,用来手动关闭弹窗,必须声明为可选
  • @State username / password—— 弹窗内部的状态,用于保存输入内容
  • onConfirm—— 回调函数,把输入结果传回调用方
  • TextInput+onChange—— 输入框的值实时同步到@State变量
  • this.controller?.close()—— 先关闭弹窗,再执行回调,避免弹窗卡住
  • this.onConfirm(this.username, this.password)—— 把用户名密码传出去

在页面中使用:

@Entry@Componentstruct LoginPage{privatedialogController:CustomDialogController=newCustomDialogController({builder:LoginDialog({onConfirm:(username:string,password:string)=>{this.doLogin(username,password);}}),alignment:DialogAlignment.Center,cornerRadius:16,width:'80%'});privatedoLogin(username:string,password:string):void{console.info(`登录:${username},${password}`);}build(){Column(){Button('登录').onClick(()=>{this.dialogController.open();})}.width('100%').height('100%').justifyContent(FlexAlign.Center)}}
  • CustomDialogController@Component内定义,builder参数指向你的弹窗组件
  • 回调函数在builder里传入,实现弹窗 → 页面的数据回传
  • alignment控制弹窗位置,DialogAlignment.Center是居中,还有BottomTop

弹窗动画

CustomDialog 支持自定义出现和消失动画:

privatedialogController:CustomDialogController=newCustomDialogController({builder:LoginDialog({onConfirm:()=>{}}),transitionAction:(transition:TransitionProxy)=>{if(transition.transitionEffect){transition.performTransition();}},showTransition:TransitionEffect.OPACITY.combine(TransitionEffect.SLIDE({direction:SlideDirection.DOWN})),hideTransition:TransitionEffect.OPACITY.combine(TransitionEffect.SLIDE({direction:SlideDirection.UP}))});
  • showTransition控制弹窗出现的动画效果
  • hideTransition控制弹窗消失的动画效果
  • TransitionEffect.OPACITY是透明度渐变,SLIDE是滑动方向
  • combine可以组合多个动画效果

弹窗嵌套处理

真做项目的时候,弹窗里弹弹窗是常事。但有个坑:CustomDialog 里不能再直接 open 另一个 CustomDialog

解决方案:setTimeout延迟打开,让第一个弹窗完全关闭后再开第二个:

Button('删除').onClick(()=>{this.controller?.close();setTimeout(()=>{this.confirmDialogController.open();},300);})
  • 300ms 的延迟是经验值,足够让前一个弹窗的关闭动画完成
  • 如果弹窗有更长的关闭动画,可以适当加大延迟

真香警告:如果嵌套层级太深(3 层以上),建议重构交互流程,用页面导航代替弹窗嵌套。

写在最后

弹窗这东西,选对类型就成功了一半。AlertDialog 能搞定的别用 CustomDialog,代码量差了十倍不止。但反过来,需要自定义内容的场景硬上 AlertDialog 也很痛苦——放不了输入框、改不了布局、控制不了样式。

我的经验是:项目一开始就封装一套弹窗工具类,三种类型各一个快捷方法,用到的时候一行调用。别在每个页面里重复写弹窗逻辑,维护起来想死。