📚往期推文全新看点(文中附带全新鸿蒙5.0全栈学习笔录)

🎯 鸿蒙(HarmonyOS)北向开发知识点记录~

🎯 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~

🎯 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?

🎯 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~

🎯 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?

🎯 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?

🎯 记录一场鸿蒙开发岗位面试经历~

🎯 持续更新中……


简述

自定义能力是ArkUI开发框架提供的对UI界面进行开发和设计的能力。现有的自定义能力包括:自定义节点。ArkUI开发框架提供的不同控制层级的自定义能力用于实现不同场景的应用的开发。自定义能力的控制层级越低接近基础能力,开发的灵活程度越高,开发难度越高、对于开发者能力的要求也越高。

  • 自定义节点:具备底层实体节点的部分基础能力的节点对象,这些节点能够通过 自定义占位节点 与原生控件进行混合显示。自定义节点可以具备单个节点的测算布局、设置基础属性、设置事件监听、自定义绘制渲染内容的自定义能力。包括 FrameNode 、 RenderNode 、 BuilderNode 三类对象。FrameNode表示了组件的实体节点,RenderNode表示更加轻量级的渲染节点,BuilderNode对象提供了能够创建、更新原生组件以及组件树的能力。

自定节点概述

自定义节点是ArkUI框架通过接口提供了底层实体节点部分基础能力的节点对象,这些节点能够与原生控件进行混合显示。

自定义节点的挂载和显示需要依赖 自定义占位节点 。现有的自定义节点包括 FrameNode 、 RenderNode 、 BuilderNode 三类对象。FrameNode表示了单个的自定义组件节点,RenderNode表示更加轻量级的渲染节点,BuilderNode对象提供了能够创建、更新原生组件以及组件树的能力。

自定义占位节点

自定义占位节点作为原生组件可以在原生组件树上为自定义节点提供挂载的点。通过自定义占位节点,可以将自定义节点挂载在占位节点上,实现自定义节点与原生组件的混合显示。

FrameNode

FrameNode表示组件的实体节点,具体可以分为两大类能力:完全自定义节点的能力以及原生组件节点代理的能力。

  • 完全自定义节点:提供完整的自定义能力,包括自定义测量、布局以及绘制,支持节点的动态增、删,设置通用属性,设置事件回调。适用于不自带渲染引擎,需要依赖系统的布局、事件、动画、渲染都能力的场景。

  • 原生组件代理节点:提供原生组件的代理能力,提供遍历节点树的能力,通过组件树上的FrameNode可以遍历整个组件树,并通过节点访问组件的信息或者注册额外的事件监听回调。适用于结合无感监听的接口实现打点、广告SDK、中台DFX等业务。

RenderNode

RenderNode作为轻量级的渲染节点,仅提供了设置渲染相关属性、自定义绘制内容以及节点操作的能力。适用于仅依赖系统渲染与动画能力的自定义场景。

BuilderNode

BuilderNode通过无状态的UI方法 全局@Builder 生成组件树,组件树内的节点为原生组件。适用于需要基于系统能力创建特定原生组件树与其他自定义节点进行混合显示的场景。相比较原生组件,BuilderNode具备预创建的优势,可以控制开始创建的时间。由于持有实体节点对象,因此可以同步实现节点的复用,通过占位节点结合FrameNode、RenderNode的节点操作能力控制显示位置。

自定义占位节点

ArkUI提供了ArkTS原生组件作为自定义节点的占位节点。该占位节点具备组件的通用属性。

NodeContainer和NodeController

NodeContainer 作为原生组件,仅具备组件的通用属性,其节点规格参考默认左上角对齐的 Stack 组件。NodeContainer作为一个占位容器组件,主要是用于显示自定义节点以及自定义节点树的显示和复用。

NodeController 提供了一系列生命周期回调,通过makeNode回调返回一个FrameNode节点树的根节点。将 FrameNode 节点树挂载到对应的NodeContainer下。同时提供了aboutToAppear、aboutToDisappear、aboutToResize、onTouchEvent、rebuild五个回调方法用于监听对应的NodeContainer的状态。

每个生命周期的回调的具体含义参考 NodeController 的接口文档说明。

说明

  • NodeContainer下仅支持挂载自定义的FrameNode节点以及BuilderNode创建的组件树的根节点。
  • 从API Version 12开始支持的接口,可以通过FrameNode的查询接接口返回原生组件的代理节点,代理节点可以作为makeNode的返回值进行返回,但代理节点无法成功挂载在组件树上,最终的显示结果为代理节点挂载失败。
  • 需要保证一个节点只能作为一个父节点的子节点去使用,否则可能存在显示异常或者功能异常,尤其是页面路由场景或者动效场景。例如,如果通过NodeController将同一个节点挂载在多个NodeContainer上,仅一个占位容器下会显示节点,且多个NodeContainer的可见性、透明度等影响子组件状态的属性更新均会影响被挂载的子节点。
import { BuilderNode, FrameNode, NodeController, Size, UIContext } from '@kit.ArkUI'

class Params {
  text: string = "this is a text"
}

@Builder
function buttonBuilder(params: Params) {
  Column() {
    Button(params.text)
      .fontSize(12)
      .borderRadius(8)
      .borderWidth(2)
      .backgroundColor(Color.Orange)
  }
}

let buttonNode: BuilderNode<[Params]> | null = null;

class MyNodeController extends NodeController {
  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(buttonBuilder);
  private isShow: boolean = false;

  constructor(isShow: boolean) {
    super();
    this.isShow = isShow;
  }

  makeNode(uiContext: UIContext): FrameNode | null {
    if (!this.isShow) {
      return null;
    }
    if (buttonNode == null) {
      buttonNode = new BuilderNode<[Params]>(uiContext);
      buttonNode.build(this.wrapBuilder, { text: "This is a Button" })
    }
    let frameNode = buttonNode?.getFrameNode();
    return frameNode ? frameNode : null;
  }

  aboutToResize(size: Size) {
    console.log("aboutToResize width : " + size.width + " height : " + size.height)
  }

  aboutToAppear() {
    console.log("aboutToAppear")
  }

  aboutToDisappear() {
    console.log("aboutToDisappear");
  }

  onTouchEvent(event: TouchEvent) {
    console.log("onTouchEvent");
  }

  toShow() {
    this.isShow = true;
    this.rebuild();
  }

  toHide() {
    this.isShow = false;
    this.rebuild();
  }
}

@Entry
@Component
struct Index {
  private myNodeController1: MyNodeController = new MyNodeController(true);
  private myNodeController2: MyNodeController = new MyNodeController(false);

  build() {
    Column() {
      NodeContainer(this.myNodeController1)
        .width("100%")
        .height("40%")
        .backgroundColor(Color.Brown)
      NodeContainer(this.myNodeController2)
        .width("100%")
        .height("40%")
        .backgroundColor(Color.Gray)
      Button("Change the place of button")
        .onClick(() => {
          // 先在原始占位节点中下树
          // 后在新的占位节点中上树
          // 保证自定义节点仅作为一个节点的子节点存在
          this.myNodeController1.toHide();
          this.myNodeController2.toShow();
        })
    }
    .padding({ left: 35, right: 35, top: 35 })
    .width("100%")
    .height("100%")
  }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐