1、背 景
在上一篇文章中(鸿蒙UI开发------实现一个上拉抽屉效果),我们讨论了如何实现一个上拉抽屉效果:
有朋友私信我说:在上拉抽屉完成后,如何实现动态更新上拉抽屉的样式,以及抽屉中的数据如何回调到对应的父组件。针对这个问题,我们做一下讨论。
示例效果如下:

2、更新抽屉样式
如果我们想更新抽屉样式,可以通过updateBindSheet来实现,updateBindSheet接口定义如下:
功能:更新bindSheetContent对应的半模态页面的样式,使用Promise异步回调。
updateBindSheet<T extends Object>(bindSheetContent: ComponentContent<T>, sheetOptions: SheetOptions, partialUpdate?: boolean ): Promise<void>
其中有三个参数,分别是:
-
bindSheetContent: 半模态页面中显示的组件内容。
-
sheetOptions: 半模态页面样式。
-
partialUpdate:半模态页面更新方式, 默认值为false。true为增量更新,保留当前值,更新SheetOptions中的指定属性。false为全量更新,除SheetOptions中的指定属性,其他属性恢复默认值。
其中sheetOptions是配置参数,我们在上一篇文章中已经有介绍,参见鸿蒙UI开发------实现一个上拉抽屉效果。
另外,bindSheetContent参数是组件内容对象(ComponentContent),构建一个ComponentContent对象,我们可以通过类似如下的方式:
contentNode = new ComponentContent(this.getUIContext(), wrapBuilder(xxxBuilderFunciton), new Params(this.message));
其中有几个重要的入参:
- uiContext,我们可以通过this.getUIContext()方法得到,
-
第二个参数是一个builder,我们可以通过wrapBuilder(xxxBuilderFunciton)构建出来(后文会有示例)。
-
第三个是可选参数,表示builder构建函数的入参(后文会有示例)。
3、回调父组件
回调父组件我们可以通过param传入callback的形式来实现,代码示例如下(第16行):
class Params { text: string = "" updateText = (str: string) => { // do nothing }; constructor(text: string, callback: (str: string) => void) { this.text = text; this.updateText = callback; }}@Builderfunction buildText(params: Params) { Column() { Button('更新父组件') .onClick(() => { params.updateText('callback: 抽屉关闭成功!') }) }}
在buildText方法中,我们的Params里面定义一个callback,通过callback可以触发上层组件的更新。
4、示 例
一个示例效果如下:

-
我们点击「打开抽屉界面」按钮后,弹出上拉抽屉并插入了上层组件的默认参数"父组件初始默认参数"文本。
-
在抽屉面板中,展示出了上层组件的插入文本;
-
在抽屉面板中,点击「将背景色改为粉色」后,抽屉面板背景色发生变化;
-
点击「点我关闭抽屉」后,抽屉收起并调用了父组件的callback,让父组件""父组件初始默认参数"文本改为了"callback: 抽屉关闭成功"文本。
代码如下(22 ~ 24行代码更新抽屉样式,36行代码回调上层组件):
import { FrameNode, ComponentContent } from "@kit.ArkUI";import { BusinessError } from '@kit.BasicServicesKit';class Params { text: string = "" updateText = (str: string) => { // do nothing }; constructor(text: string, callback: (str: string) => void) { this.text = text; this.updateText = callback; }}let contentNode: ComponentContent<Params>;let uiContext: UIContext;@Builderfunction buildText(params: Params) { Column() { Text(params.text).margin(10) Button('将背景色改为粉色') .fontSize(20) .onClick(() => { uiContext.updateBindSheet(contentNode, { backgroundColor: Color.Pink, }, true) .then(() => console.info('更新抽屉成功')) .catch((err: BusinessError) => { console.info('更新抽屉错误: ' + err.code + ' ' + err.message); }) }) .margin(10) Button('点我关闭抽屉') .fontSize(20) .onClick(() => { uiContext.closeBindSheet(contentNode) .then(() => { params.updateText('callback: 抽屉关闭成功!') console.info('关闭抽屉成功'); }) .catch((err: BusinessError) => { console.info('关闭抽屉失败: ' + err.code + ' ' + err.message); }) }) }}@Entry@Componentstruct UIContextBindSheet { @State message: string = '父组件初始默认参数'; aboutToAppear() { uiContext = this.getUIContext(); contentNode = new ComponentContent(this.getUIContext(), wrapBuilder(buildText), new Params(this.message, (str: string) => { this.message = str; })); } build() { RelativeContainer() { Column() { Text('下面是父组件的入参文本').margin(10) Text(this.message).margin(10) Button('打开抽屉界面') .fontSize(20) .onClick(() => { let uiContext = this.getUIContext(); let uniqueId = this.getUniqueId(); let frameNode: FrameNode | null = uiContext.getFrameNodeByUniqueId(uniqueId); let targetId = frameNode?.getFirstChild()?.getUniqueId(); uiContext.openBindSheet(contentNode, { height: SheetSize.MEDIUM, backgroundColor: Color.White, title: { title: "标题", subtitle: "副标题" } }, targetId) .then(() => { console.info('抽屉界面打开成功!'); }) .catch((err: BusinessError) => { console.info('打开抽屉失败: ' + err.code + ' ' + err.message); }) }) }.width('100%') } .height('100%') .width('100%') }}