1、概 述
在项目开发中,我们可能会遇到临时交互的场景(即:弹出一个临时交互框,交互完毕后继续用户的主流程),效果如下:

在ArkUI中,此类弹出窗被称为"半模态页面",ArkUI为我们提供了一套可以快速实现此效果的接口,本文针对此效果的实现做讨论。
2、接口定义
给组件绑定半模态页面,点击后显示模态页面。接口定义如下:
bindSheet(isShow: Optional<boolean>, builder: CustomBuilder, options?: SheetOptions)
用法类似如下(第5行代码):
build() { Column() { Button("点击我弹出上拉抽屉") // ... 其他属性 .bindSheet($$this.isShow, this.myBuilder(), { // 三挡高度(默认是中间) detents: [SheetSize.MEDIUM, SheetSize.LARGE, 200], blurStyle: BlurStyle.Thick, showClose: true, title: { title: "抽屉标题", subtitle: "抽屉副标题" }, }) } .justifyContent(FlexAlign.Start) .width('100%') .height('100%')}
其中:
-
isShow是可以支持双向绑定的显示/隐藏的切换控制;
-
builder是抽屉需要显示的内容,内容构造器;
-
options是配置参数,定义如下:
|-----------------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 名称 | 类型 | 说明 |
| backgroundColor | ResourceColor | 半模态页面的背板颜色。默认值:Color.White。 |
| onWillAppear | () => void | 半模态页面显示(动画开始前)回调函数。 |
| onAppear | () => void | 半模态页面显示(动画结束后)回调函数。 |
| onWillDisappear | () => void | 半模态页面回退(动画开始前)回调函数。 说明:不允许在onWillDisappear函数中修改状态变量,可能会导致组件行为不稳定。 |
| onDisappear | () => void | 半模态页面回退(动画结束后)回调函数。 |
| height | SheetSize | Length | 半模态高度,默认是LARGE。 说明:API version 12之前,底部弹窗横屏时该属性设置无效,高度为距离屏幕顶部8vp。API version 12开始,底部弹窗横屏时该属性设置生效,最大高度为距离屏幕顶部8vp。底部弹窗时,当设置detents时,该属性设置无效。底部弹窗竖屏时,最大高度为距离信号栏8vp。居中弹窗和跟手弹窗设置类型为SheetSize.LARGE和SheetSize.MEDIUM无效,显示默认高度560vp。居中弹窗和跟手弹窗最小高度为320vp,最大高度为窗口短边的90%。当使用Length设置的高度和使用SheetSize.FIT_CONTENT自适应的高度大于最大高度,则显示最大高度,小于最小高度,则显示最小高度。 |
| detents | [(SheetSize | Length), ( SheetSize | Length)?, (SheetSize | Length)?] | 半模态页面的切换高度档位。 说明:从API version 12开始,底部弹窗横屏时该属性设置生效。底部弹窗竖屏生效,元组中第一个高度为初始高度。面板可跟手滑动切换档位,松手后是否滑动至目标档位有两个判断条件:速度和距离。速度超过阈值,则执行滑动至与手速方向一致的目标档位;速度小于阈值,则引入距离判断条件,当位移距离>当前位置与目标位置的1/2,滑动至与手速方向一致的目标档位,位移距离当前位置与目标位置的1/2,返回至当前档位。速度阈值:1000,距离阈值:50%。 |
| preferType | SheetType | 半模态页面的样式。 说明:半模态在不同窗口所支持的显示类型:1. 宽度 < 600vp:底部。2. 600vp <= 宽度 < 840vp:底部、居中。默认居中样式。3. 宽度 >= 840vp:底部、居中、跟手。默认跟手样式。 |
| showClose | boolean | Resource | 是否显示关闭图标,默认显示。 说明:Resource需要为boolean类型。 |
| dragBar | boolean | 是否显示控制条。 说明:半模态面板的detents属性设置多个不同高度并且设置生效时,默认显示控制条。否则不显示控制条。 |
| blurStyle | BlurStyle | 半模态面板的模糊背景。默认无模糊背景。 |
| maskColor | ResourceColor | 半模态页面的背景蒙层颜色。 |
| title | SheetTitleOptions | CustomBuilder | 半模态面板的标题。 |
| enableOutsideInteractive | boolean | 半模态所在页面是否允许交互。 说明:设置为true时允许交互,不显示蒙层;设置为false时不允许交互,显示蒙层;若不进行设置,默认底部弹窗与居中弹窗不允许交互,跟手弹窗允许交互。当设置为true时,maskColor设置无效。 |
| shouldDismiss | (sheetDismiss: SheetDismiss) => void | 半模态页面交互式关闭回调函数。 说明:当用户执行下拉关闭/back事件/点击蒙层关闭/关闭按钮关闭交互操作时,如果注册该回调函数,则不会立刻关闭。 |
| onWillDismiss | DismissSheetAction | 半模态页面交互式关闭回调函数。说明:当用户执行关闭操作时,如果注册该回调函数,不会立刻关闭, 由开发者控制是否关闭。在回调函数中可以通过reason得到关闭页面的操作类型,从而根据原因选择是否关闭半模态页面。在onWillDismiss回调中,不能再做onWillDismiss拦截。元服务API: 从API version 12开始,该接口支持在元服务中使用。 |
| onWillSpringBackWhenDismiss | SpringBackAction | 半模态页面交互式关闭前控制回弹函数。 说明:当用户执行下拉关闭操作并注册shouldDimiss或onWillDismiss时,如果注册该回调函数,则不会回弹,由开发者控制下滑关闭时是否回弹。在回调函数中可以通过调用springBack来实现回弹效果。 |
| onHeightDidChange | Callback<number> | 半模态页面高度变化回调函数。 说明:底部弹窗时,只有档位变化和拖拽跟手才返回每一帧高度,拉起半模态和避让软键盘只返回最后的高度,其他弹窗只在半模态拉起返回最后高度。返回值为px。 |
| onDetentsDidChange | Callback<number> | 半模态页面档位变化回调函数。 说明:底部弹窗时,档位变化返回最后的高度。返回值为px。 |
| onWidthDidChange | Callback<number> | 半模态页面宽度变化回调函数。 说明:宽度变化时返回最后的宽度。返回值为px。 |
| onTypeDidChange | Callback<SheetType> | 半模态页面形态变化回调函数。 说明:形态变化时返回最后的形态。 |
| borderWidth | Dimension | EdgeWidths | LocalizedEdgeWidths | 设置半模态页面的边框宽度。可分别设置4个边框宽度。默认值:0。百分比参数方式:以父元素半模态页面宽的百分比来设置半模态页面的边框宽度。当半模态页面左边框和右边框大于半模态页面宽度,半模态页面上边框和下边框大于半模态页面高度,显示可能不符合预期。说明:底部弹窗时,底部边框宽度设置无效。 |
| borderColor | ResourceColor | EdgeColors | LocalizedEdgeColors | 设置半模态页面的边框颜色。默认值:Color.Black。如果使用borderColor属性,需要和borderWidth属性一起使用。说明:底部弹窗时,底部边框颜色设置无效。 |
| borderStyle | BorderStyle | EdgeStyles | 设置半模态页面的边框样式。默认值:BorderStyle.Solid。如果使用borderStyle属性,需要和borderWidth属性一起使用。说明:底部弹窗时,底部边框样式设置无效。 |
| width | Dimension | 设置半模态页面的宽度。百分比参数方式:以父元素宽的百分比来设置半模态页面的宽度。 |
| shadow | ShadowOptions | ShadowStyle | 设置半模态页面的阴影。 |
| uiContext | UIContext | 在UIContext实例对应的窗口中显示半模态。说明:使用openBindSheet启动的半模态页面,不支持设置、更新该属性。元服务API: 从API version 12开始,该接口支持在元服务中使用。 |
| mode | SheetMode | 设置半模态页面的显示层级。默认值:SheetMode.OVERLAY。 说明:1. 半模态显示期间mode属性不支持动态切换,两种模式的显示层级完全不同,无法做到显示期间同一个半模态从一个层级变换到另一个层级。建议在使用时明确诉求固定mode值。2. 设置SheetMode.EMBEDDED时不支持设置UIContext属性,两者对应的半模态显示层级效果互相冲突。3. 使用openBindSheet启动半模态页面,若未传入有效的targetId,则不支持设置为SheetMode.EMBEDDED,默认为SheetMode.OVERLAY。元服务API: 从API version 12开始,该接口支持在元服务中使用。 |
| scrollSizeMode | ScrollSizeMode | 设置半模态面板滑动时,内容区域刷新时机。默认值:ScrollSizeMode.FOLLOW_DETENT 。 |
3、案 例
实现一个案例效果如下:

代码如下(24~30行的配置,5~15行的builder):
@Entry@Componentstruct SheetTransitionExample { @State isShow: boolean = false @Builder myBuilder() { Column() { Text('欢迎加入【Harmony自习室】') .fontSize(20) Button("确认") .margin(10) .fontSize(20) } .width('100%') } build() { Column() { Button("点击我弹出上拉抽屉") .onClick(() => { this.isShow = true }) .fontSize(20) .margin(10) .bindSheet($$this.isShow, this.myBuilder(), { // 三挡高度(默认是中间) detents: [SheetSize.MEDIUM, SheetSize.LARGE, 200], blurStyle: BlurStyle.Thick, showClose: true, title: { title: "抽屉标题", subtitle: "抽屉副标题" }, }) } .justifyContent(FlexAlign.Start) .width('100%') .height('100%') }}