引言
在鸿蒙开发中使用 CustomDialogController 和 @CustomDialog 可实现自定义弹窗交互。但 controller 的定义位置却有很大的限制。
限制如下:
CustomDialogController仅在作为@CustomDialog
和@Component struct
的成员变量,且在@Component struct内部定义时赋值才有效
对于 Dialog 的能力封装我们通常会将其与调用者页面解耦,以 Flutter 为例,我们可能会这么封装:
typedef DialogCallback = Function(String data);
// 随便一个工具类
abstract class DialogTool {
static void showCustomDialog(
BuildContext context,
DialogCallback callback, // 定义回调
) {
showDialog(
context: context,
builder: (context) {
return Dialog(
child: YourWidget(), // dialog UI
);
},
).then(
(data) => {
callback(data),
},
);
}
}
在工具类提供静态方法,调用方可直接调用并获取返回值。
而在 ArkTS 页面内开发一个自定义弹窗,不可避免的会有一堆冗余代码需要被嵌在调用者UI层,下面我来举个例子:
- 首先使用 @CustomDialog 定义一个简单的带回调的 Dialog
@CustomDialog
export struct MyCustomDialog {
private controller: CustomDialogController
//确定按钮回调
confirmed: (message: string) => void = (_) => {
}
build() {
Column() {
Text('这是一个自定义弹窗')
Button('点击传递数据给调用方').onClick(() => {
this.controller.close()
this.confirmed('你好,我是弹窗点击返回的内容')
})
}
}
}
- 调用者页面需要定义 CustomDialogController 绑定 MyCustomDialog 来唤起自定义弹窗
@Component
struct HomePage {
dialogController?: CustomDialogController | null
private async showDialog(): Promise<string | undefined> {
let resolveFunc: (value: string | undefined | PromiseLike<string | undefined>) => void = () => {
}
const promise = new Promise<string | undefined>(
(resolve, _) => {
resolveFunc = resolve
}
)
this.dialogController = new CustomDialogController({
builder: MyCustomDialog(
{
confirmed: (info) => {
resolveFunc(info)
},
}
)
})
this.dialogController?.open()
return promise
}
build() {
Column(){
... 你的UI布局
}
}
}
如上,页面内冗余代码主要体现在 showDialog(){}
内,当你的页面需要具备多种业务的自定义弹窗时,你就需要在同一个页面定义多个 showDialog() 方法用以显示不同的弹窗,接收不同的回执传参。
整体的代码会变得很乱,结构封装会失去简洁性。
如果不能避开限制,怎么做解耦?
我们实践的方案是做一个空白中间层,用于存放 CustomDialogController 与 CustomDialog 的绑定工作(代码),只给上层UI提供一个 controller 用来控制弹窗开启、关闭、以及接收返回值。以此达到与调用方浅解耦的目的。
- 已上文中的 MyCustomDialog 为例,新建一个文件 CustomDialogContainer, 代码如下:
/// 为了将dialog的初始化与与使用dialog的页面解耦
@Component
export struct CustomDialogContainer {
@Link @Watch('onDialogToggle') controller: DialogContainerController
dialogController?: CustomDialogController | null
onDialogToggle() {
if (this.controller.isOpen) {
this.showDialog().then((data) => {
this.controller.onDialogConfirm(data)
})
} else {
this.dialogController?.close()
}
}
private async showDialog(): Promise<string | undefined> {
let resolveFunc: (value: string | undefined | PromiseLike<string | undefined>) => void = () => {
}
const promise = new Promise<string | undefined>(
(resolve, _) => {
resolveFunc = resolve
}
)
this.dialogController = new CustomDialogController({
builder: MyCustomDialog(
{
confirmed: (info) => {
resolveFunc(info)
},
}
),
})
this.dialogController?.open()
return promise
}
build() {
}
}
export class DialogContainerController {
isOpen: boolean = false;
onDialogConfirm: (value: string | undefined) => void
constructor(onConfirm: (value: string | undefined) => void) {
this.onDialogConfirm = onConfirm
}
public open() {
this.isOpen = true
}
public close() {
this.isOpen = false
}
}
从 build(){}
可以看出这是一个空白的 Component 组件。
在调用者页面我们这样使用
@Component
export struct HomePage {
@State dialogController: DialogContainerController = new DialogContainerController(
(message: string | undefined) => {
// 这里接收到弹窗内确定按钮点击返回的数据
if (message != undefined) {
// 你的业务逻辑
}
}
)
// 如果有多个样式的弹窗,定义多个 dialogController 然后统一在这里管理
@Builder
DialogControllerBuilder() {
Column() {
CustomDialogContainer({
controller: this.dialogController
})
}
}
build() {
Column() {
this.DialogControllerBuilder()
... 你的UI
}
}
}
调用者业务只持有自定义弹窗的 controller 处理交互,如果有多个自定义弹窗,则定义多个 controller ,统一存放管理,在一定程度内减少了独立业务的代码冗余。
附注(Example)
Demo 示例已上传:
GitHub:https://github.com/liyufengrex/HarmonyAtomicService
GitCode:https://gitcode.com/liyufengrex/HarmonyAtomicService
(基于API11开发,支持NEXT及以上版本运行)已上传可供参考,包含如下内容:
- 静态库+动态包+多模块设计
- 状态管理
- 统一路由管理(router+navPathStack)
- 网络请求、Loading 等工具库封装
- 自定义组件、自定义弹窗(解耦)
- EventBus 事件通知
- 扩展修饰器,实现 节流、防抖、权限申请
- 动态路由 (navPathStack + 动态import + WrappedBuilder)
- UI动态节点操作 (BuilderNode + NodeController)
- 折叠屏适配示例
- 组件工厂示例
- 组件动态属性设置示例
- 云函数、云数据库使用示例
- 华为账号服务示例(快速登陆、快速验证手机号)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。