头图

HarmonyOS Next 主动调用自定义控件中封装的方法最佳实践

1、背景介绍

HarmonyOS Next主要开发语言是ArkTS,语言框架ArkUI是声明式UI,声明式UI一般不直接操作控件,而是通过状态变量更新来驱动UI刷新。但是有些场景,只通过状态变量驱动UI刷新无法满足我们复杂场景下的业务逻辑,需要在父布局调用子控件中的方法,这种情况怎么处理呢?

2、系统方式参考

系统提供的控件,比如对话框Dialog、计时器控件TextTimer、文本输入控件TextArea等,在构造时要求传入controller,通过controller在父控件中直接调用子控件中的方法。
TextTimerController提供了start、pause、reset方法供父组件调用,CustomDialogController提供了open和close方法供父组件调用,一次来达到操作子组件的效果。

我们可以参考类似思路,为子组件封装Controller供父组件调用。

3、调用子组件方式封装

父组件调用子组件对外提供的Controller控制器,实现调用控制器方法来调用子组件的方法。通过这种方法给子组件封装一些能力,比如数据处理,刷新,动画,发通知等。

具体实现方式如下:

  1. 定义一个ChildController类,在其中定义和子组件中类型相同的方法,例如:
class ChildController { 
    changeText = (value: string) => { console.log('11111') } 
} 
export let ChildRef = new ChildController()
  1. 在子组件中,将实际封装的方法给到controller,在aboutToAppear中,将子组件的方法赋值给controller中的方法变量,以此达到父组件调用Controller方法时实际调用的是子组件的方法。例如:

    @Component
    struct Child {
      @State private text: string = '初始值';
      private controller: ChildController = new ChildController();
    
      aboutToAppear() {
     if (this.controller) {
       this.controller.changeText = this.changeText;
     }
     console.log('aaa');
      }
    
      private changeText = (value: string) => {
     this.text = value;
     console.log('bbb');
      }
    
      build() {
     Column() {
       Text(this.text);
     }
      }
    }
  2. 在父组件中,新建一个controller对象然后传入子组件中,在父组件中调用controller对应的方法即可。例如:

    @Entry
    @Component
    struct Parent {
      private ChildRef = new ChildController();
      build() {
     Column() {
       Text('调用 Child 的 changeText').fontSize('18vp').fontColor(Color.Gray);
       Divider();
       Child({ controller: this.ChildRef });
     }
      }
    }

4、设计方式分析

这种方式的原理主要是通过创建一个中间的 controller 对象来实现间接的通信。在父组件中持有这个 controller 对象,子组件在初始化时将自身的实际方法赋值给 controller 对象中对应的方法引用。这样,当父组件调用 controller 对象的方法时,实际上是在调用子组件中已经赋值过的方法。这实现了一种松耦合的通信方式,避免了父组件直接依赖子组件的内部实现,提高了代码的可维护性和可扩展性。

5、总结

本文介绍了在声明式UI框架ArkUI中实现父组件调用子组件的实现方法,通过松耦合通信方式,解决父组件无法持有子组件直接调用子组件的难题。


轻口味
28.1k 声望4.5k 粉丝

移动端十年老人,主要做IM、音视频、AI方向,目前在做鸿蒙化适配,欢迎这些方向的同学交流:wodekouwei