鸿蒙UI开发——上拉抽屉的更新与事件回调
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;
}
}
@Builder
function 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;
@Builder
function 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
@Component
struct 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%')
}
}