第10篇半模态与弹窗交互在 HarmonyOS 应用开发中半模态Half Modal和自定义弹窗是两种常见的高频交互形态。半模态从页面底部滑出不会完全遮挡底层内容适合表单填写、筛选等场景自定义弹窗则用于确认、提醒等强反馈交互。本文结合柚兔学伴项目中TodoView.ets的真实代码详解这两种交互的实现方式。一、bindSheet 半模态绑定HarmonyOS 提供了bindSheet修饰符可以将任意自定义 Builder 以半模态面板的形式绑定到组件上。当绑定的状态变量为true时面板从底部弹出为false时自动收起。1.1 基本用法在项目中TodoView通过isShowTask状态变量控制任务添加面板的显示与隐藏StateisShowTask:booleanfalseStatesheetHeight:number300;点击添加按钮时将isShowTask置为trueImage($r(app.media.ic_add)).width(20).onClick((){this.isShowTasktrue})然后在Scroll组件上绑定半模态Scroll(this.scroller){// ...页面内容}.bindSheet($$this.isShowTask,this.todoAddBuilder(),{blurStyle:BlurStyle.Thick,dragBar:true,title:this.DialogTitle(今日任务,添加,async(){// 添加逻辑}),height:this.sheetHeight,keyboardAvoidMode:SheetKeyboardAvoidMode.TRANSLATE_AND_RESIZE})1.2 关键参数解析参数说明$$this.isShowTask双向绑定状态变量$$语法确保半模态内部如拖拽关闭也能同步修改状态this.todoAddBuilder()自定义 Builder作为半模态的内容区blurStyle底层页面的模糊样式BlurStyle.Thick表示深度模糊dragBar是否显示顶部拖拽条方便用户下拉关闭title半模态标题栏支持自定义 Builderheight半模态高度可动态设置keyboardAvoidMode键盘避让模式其中keyboardAvoidMode: SheetKeyboardAvoidMode.TRANSLATE_AND_RESIZE是表单场景的关键配置——当软键盘弹出时半模态面板会同时平移和缩放确保输入框不被遮挡。二、自定义 Sheet 标题栏半模态的title参数不仅支持纯文本还支持传入自定义 Builder。项目中实现了一个DialogTitleBuilder左侧显示标题文字右侧放置操作按钮BuilderDialogTitle(title:string,btnText:string,onClick:()void){Row(){Text(title).fontWeight(FontWeight.Bold)Button(btnText).backgroundColor(Color.Blue).fontColor(Color.White).onClick(onClick)}.width(100%).justifyContent(FlexAlign.SpaceBetween)}在bindSheet中使用title:this.DialogTitle(今日任务,添加,async(){if(StrUtil.isEmpty(this.value)){ToastUtil.showToast(任务内容不能为空)return}if(StrUtil.isEmpty(this.startTime)){ToastUtil.showToast(开始时间不能为空)return}if(StrUtil.isEmpty(this.stopTime)){ToastUtil.showToast(结束时间不能为空)return}// 验证通过添加待办事项constnewTodo:TodoItem{date:DateUtil.getTodayStr(yyyy-MM-dd),dayOfWeek:周DateUtil.getWeekDay(DateUtil.getTodayStr(HH-mm-ss)),time:${this.startTime}-${this.stopTime},content:this.value,isCompleted:false};consttodoIdawaitthis.todoDb.addTodo(newTodo);this.arrawaitthis.todoDb.getAllTodos();this.startTimethis.stopTimethis.isShowTaskfalsethis.isTodoEmptyfalse})这种设计将表单验证逻辑集中在标题栏按钮的回调中验证通过后关闭面板isShowTask false验证失败则通过 Toast 提示用户。三、IBestField 输入组件项目使用了ibestservices/ibest-ui三方库提供的IBestField组件作为输入框。它内置了标签、边框、圆角等样式配置IBestField({label:任务名称,value:$value,showLabel:false,placeholder:请输入今日任务,hasBorder:false,radius:12,onChange:(input){this.valueinput.toString()}}).width(100%)value: $value通过$语法进行双向绑定输入内容自动同步到this.valueshowLabel: false隐藏左侧标签因为半模态空间有限hasBorder: false去除默认边框与面板整体风格统一onChange内容变化时的回调四、IBestCell showDialogTime 时间选择时间选择采用IBestCell单元格showDialogTime时间弹窗的组合模式。IBestCell以列表项的形式展示当前值点击后弹出时间选择器IBestCell({title:开始时间,value:this.startTime?this.startTime:请选择开始时间,isLink:true,onCellClick:((){showDialogTime({titleBarAttribute:{titleText:请选择开始时间,backgroundColor:$r(app.color.color_card)},timeAttribute:{timeType:TimeDialogType.HM,startHours:6,startMinutes:0,endHours:22,endMinutes:59,},backgroundColor:$r(app.color.color_card),timeConfirmClick:(date){letformatternewIntl.DateTimeFormat(zh-CN,{hour:2-digit,minute:2-digit,hour12:false});letresult:stringformatter.format(date);this.startTimeresult},confirmClick:(value,index){console.log(内容结果valueindex)}})})})4.1 TimeDialogTypeabner/dialog库的showDialogTime支持TimeDialogType枚举TimeDialogType.HM仅选择时和分小时:分钟适合任务时间选择TimeDialogType.HMS选择时、分、秒适合倒计时精确设置4.2 Intl.DateTimeFormat 时间格式化项目使用 JavaScript 内置的Intl.DateTimeFormatAPI 进行时间格式化letformatternewIntl.DateTimeFormat(zh-CN,{hour:2-digit,minute:2-digit,hour12:false});letresult:stringformatter.format(date);this.startTimeresultzh-CN中文 locale确保格式符合中国用户习惯hour12: false强制 24 小时制避免上下午歧义2-digit两位数显示如08:00而非8:0五、IBestDialog 自定义弹窗IBestDialog是ibestservices/ibest-ui提供的自定义弹窗组件支持通过$$双向绑定控制可见性并通过defaultBuilder自定义内容区。5.1 倒计时结束弹窗StatealarmVisible:booleanfalseIBestDialog({visible:$alarmVisible,defaultBuilder:():voidthis.alarmBuild(),onConfirm:((){this.alarmVisiblefalsethis.timerController.reset()})})弹窗内容通过alarmBuildBuilder 定义包含闹钟动画和提示文字BuilderalarmBuild(){Column({space:15}){Lottie({controller:this.alarmController,animationPath:lottie/lottie_alarm.json,autoPlay:true,loop:true,}).width(80).height(80)Text(时间到).fontColor($r(app.color.app_primary)).fontWeight(FontWeight.Bold)}.padding(15)}5.2 任务完成弹窗StatefinishVisible:booleanfalseIBestDialog({visible:$finishVisible,defaultBuilder:():voidthis.finishBuild(),onConfirm:((){this.finishVisiblefalsethis.timerController.reset()})})两个弹窗的触发时机不同alarmVisible倒计时归零时设为truefinishVisible所有任务完成时设为true// 倒计时结束onTimerFinishedasync(){this.alarmVisibletrue}// 任务切换时检查onToggleChange:(value){item.isCompletedvalue;this.finishVisiblethis.arr.every(itemitem.isCompletedtrue);}六、表单验证模式项目使用pura/harmony-utils提供的StrUtil.isEmpty方法进行非空校验形成三级验证链if(StrUtil.isEmpty(this.value)){ToastUtil.showToast(任务内容不能为空)return}if(StrUtil.isEmpty(this.startTime)){ToastUtil.showToast(开始时间不能为空)return}if(StrUtil.isEmpty(this.stopTime)){ToastUtil.showToast(结束时间不能为空)return}这种逐项验证 提前返回的模式确保用户能明确知道哪个字段未填写而非一次性弹出所有错误。七、小结交互方式适用场景核心API半模态 bindSheet表单填写、筛选bindSheet($$state, builder, options)时间弹窗 showDialogTime时间选择TimeDialogType.HM / HMS自定义弹窗 IBestDialog确认、提醒、庆祝visible: $$state, defaultBuilder表单验证数据完整性StrUtil.isEmpty ToastUtil关键设计要点**双向绑定∗∗半模态和弹窗的‘visible‘都需要使用‘ 双向绑定**半模态和弹窗的 visible 都需要使用 双向绑定∗∗半模态和弹窗的‘visible‘都需要使用‘ 语法确保组件内部的关闭操作能同步到外部状态键盘避让半模态内包含输入框时务必设置keyboardAvoidMode验证前置在关闭半模态之前完成验证避免无效数据入库弹窗分层根据业务含义闹钟提醒 vs 完成庆祝使用不同的弹窗实例和动画内容