写点什么

HarmonyOS :自定义弹窗(CustomDialog)的解耦实践

作者:李小轰
  • 2024-11-29
    北京
  • 本文字数:2713 字

    阅读完需:约 9 分钟

引言

在鸿蒙开发中使用 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


@CustomDialogexport struct MyCustomDialog {  private controller: CustomDialogController  //确定按钮回调  confirmed: (message: string) => void = (_) => {  }
build() { Column() { Text('这是一个自定义弹窗') Button('点击传递数据给调用方').onClick(() => { this.controller.close() this.confirmed('你好,我是弹窗点击返回的内容') }) } }}
复制代码


  • 调用者页面需要定义 CustomDialogController 绑定 MyCustomDialog 来唤起自定义弹窗


@Componentstruct 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() 方法用以显示不同的弹窗,接收不同的回执传参。


整体的代码会变得很乱,结构封装会失去简洁性。

如果不能避开限制,怎么做解耦?

我们实践的方案是做一个空白中间层,用于存放 CustomDialogControllerCustomDialog 的绑定工作(代码),只给上层 UI 提供一个 controller 用来控制弹窗开启、关闭、以及接收返回值。以此达到与调用方浅解耦的目的。


  • 已上文中的 MyCustomDialog 为例,新建一个文件 CustomDialogContainer, 代码如下:


/// 为了将dialog的初始化与与使用dialog的页面解耦@Componentexport 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 组件。

在调用者页面我们这样使用

@Componentexport 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)

  • 折叠屏适配示例

  • 组件工厂示例

  • 组件动态属性设置示例

  • 云函数、云数据库使用示例

  • 华为账号服务示例(快速登陆、快速验证手机号)


用户头像

李小轰

关注

有趣的灵魂,拒绝沉默 2022-03-28 加入

目前从事 Android、Flutter、HarmonyOS 开发工作,喜欢尝试各种新玩意儿。重度强迫症(匠心精神),乐于分享!『 OpenHarmony 三方库贡献者 』、『 pub.dev 三方库维护者 』

评论

发布
暂无评论
HarmonyOS :自定义弹窗(CustomDialog)的解耦实践_HarmonyOS NEXT_李小轰_InfoQ写作社区