> 技术文档 > HarmonyOS核心知识点:三方框架之RenderNode_rsrendernodedrawableadapter::ongenerate, node type

HarmonyOS核心知识点:三方框架之RenderNode_rsrendernodedrawableadapter::ongenerate, node type


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

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

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

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

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

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

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

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

🎯 持续更新中……


概述

对于不具备自己的渲染环境的三方框架,虽然实现了前端的解析以及布局、事件等处理,但需要依赖系统提供的基础渲染、动画的能力。 FrameNode 上的通用属性、通用事件对这一类框架是多余的,会进行多次冗余的操作,包括布局、事件等处理逻辑。

RenderNode 是更加轻量级的渲染节点,仅包含渲染相关的能力。在该节点上暴露了设置基础的渲染属性的能力,并提供节点的动态增加、删除能力以及自定义绘制的能力。可以向三方框架提供基础的渲染、动画能力。

创建和删除节点

RenderNode提供了节点创建和删除的能力。可以通过RenderNode的构造函数创建自定义的RenderNode节点。通过构造函数创建的节点对应一个实体的节点。同时,可以通过RenderNode中的dispose接口来实现与实体节点的绑定关系的解除。

操作节点树

RenderNode提供了节点的增、删、查、改的能力,能够修改节点的子树结构;可以对所有RenderNode的节点的父子节点做出查询操作,并返回查询结果。

说明

  • RenderNode中查询获取得到的子树结构按照开发通过RenderNode的接口传递的参数构建。

  • RenderNode如果要与原生组件结合显示,使用需要依赖FrameNode中获取的RenderNode进行挂载上树。

设置和获取渲染相关属性

RenderNode中可以设置渲染相关的属性,包括:backgroundColor,clipToFrame,opacity,size,position,frame,pivot,scale,translation,rotation,transform,shadowColor,shadowOffset,shadowAlpha,shadowElevation,shadowRadius,borderStyle,borderWidth,borderColor,borderRadius,shapeMask。

说明

  • RenderNode中查询获取得到的属性为设置的属性值。
  • 若未传入参数或者传入参数为非法值则查询获得的为默认值。
import { FrameNode, NodeController, RenderNode } from \'@kit.ArkUI\';const renderNode = new RenderNode();renderNode.frame = { x: 0, y: 0, width: 200, height: 350 };renderNode.backgroundColor = 0xffff0000;for (let i = 0; i  { const child = renderNode.getChild(1); const nextSibling = child!.getNextSibling() if (child === null || nextSibling === null) { console.log(\'the child or nextChild is null\'); } else { // 获取子节点的位置信息 console.log(`the position of child is x: ${child.position.x}, y: ${child.position.y}, ` + `the position of nextSibling is x: ${nextSibling.position.x}, y: ${nextSibling.position.y}`); } }) } }}

自定义绘制

通过重写RenderNode中的 draw 方法,可以自定义RenderNode的绘制内容,通过 invalidate 接口可以主动触发节点的重新绘制。

说明

  • 同时同步触发多个invalidate仅会触发一次重新绘制。
  • 自定义绘制有两种绘制方式:通过ArkTS接口进行调用和通过Node-API进行调用。

ArkTS接口调用示例:

import { FrameNode, NodeController, RenderNode } from \'@kit.ArkUI\';import { drawing } from \'@kit.ArkGraphics2D\';class MyRenderNode extends RenderNode { draw(context: DrawContext) { // 获取canvas对象 const canvas = context.canvas; // 创建笔刷 const brush = new drawing.Brush(); // 设置笔刷颜色 brush.setColor({ alpha: 255, red: 255, green: 0, blue: 0 }); canvas.attachBrush(brush); // 绘制矩阵 canvas.drawRect({ left: 0, right: 200, top: 0, bottom: 200 }); canvas.detachBrush(); }}const renderNode = new MyRenderNode();renderNode.frame = { x: 0, y: 0, width: 300, height: 300 };renderNode.backgroundColor = 0xff0000ff;renderNode.opacity = 0.5;class MyNodeController extends NodeController { private rootNode: FrameNode | null = null; makeNode(uiContext: UIContext): FrameNode | null { this.rootNode = new FrameNode(uiContext); const rootRenderNode = this.rootNode?.getRenderNode(); if (rootRenderNode !== null) { rootRenderNode.frame = { x: 0, y: 0, width: 500, height: 500 } rootRenderNode.appendChild(renderNode); } return this.rootNode; }}@Entry@Componentstruct Index { private myNodeController: MyNodeController = new MyNodeController(); build() { Row() { NodeContainer(this.myNodeController) .width(\'100%\') Button(\'Invalidate\') .onClick(() => { // 同步调用多次,仅触发一次重绘 renderNode.invalidate(); renderNode.invalidate(); }) } }}

Node-API调用示例:

C++侧可通过Node-API来获取Canvas,并进行后续的自定义绘制操作。

// native_bridge.cpp#include \"napi/native_api.h\"#include #include #include #include static napi_value OnDraw(napi_env env, napi_callback_info info){ size_t argc = 4; napi_value args[4] = { nullptr }; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); int32_t id; napi_get_value_int32(env, args[0], &id); // 获取 Canvas 指针 void* temp = nullptr; napi_unwrap(env, args[1], &temp); OH_Drawing_Canvas *canvas = reinterpret_cast(temp); // 获取 Canvas 宽度 int32_t width; napi_get_value_int32(env, args[2], &width); // 获取 Canvas 高度 int32_t height; napi_get_value_int32(env, args[3], &height); // 传入canvas、height、width等信息至绘制函数中进行自定义绘制 auto path = OH_Drawing_PathCreate(); OH_Drawing_PathMoveTo(path, width / 4, height / 4); OH_Drawing_PathLineTo(path, width * 3 / 4, height / 4); OH_Drawing_PathLineTo(path, width * 3 / 4, height * 3 / 4); OH_Drawing_PathLineTo(path, width / 4, height * 3 / 4); OH_Drawing_PathLineTo(path, width / 4, height / 4); OH_Drawing_PathClose(path); auto pen = OH_Drawing_PenCreate(); OH_Drawing_PenSetWidth(pen, 10); OH_Drawing_PenSetColor(pen, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0x00, 0x00)); OH_Drawing_CanvasAttachPen(canvas, pen); OH_Drawing_CanvasDrawPath(canvas, path); return nullptr;}EXTERN_C_STARTstatic napi_value Init(napi_env env, napi_value exports){ napi_property_descriptor desc[] = { { \"nativeOnDraw\", nullptr, OnDraw, nullptr, nullptr, nullptr, napi_default, nullptr } }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports;}EXTERN_C_ENDstatic napi_module demoModule = { .nm_version =1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Init, .nm_modname = \"entry\", .nm_priv = ((void*)0), .reserved = { 0 },};extern \"C\" __attribute__((constructor)) void RegisterEntryModule(void){ napi_module_register(&demoModule);}

修改工程中的src/main/cpp/CMakeLists.txt文件,添加如下内容:

# the minimum version of CMake.cmake_minimum_required(VERSION 3.4.1)project(NapiTest)set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})include_directories(${NATIVERENDER_ROOT_PATH}  ${NATIVERENDER_ROOT_PATH}/include)add_library(entry SHARED native_bridge.cpp)target_link_libraries(entry PUBLIC libace_napi.z.so)target_link_libraries(entry PUBLIC libace_ndk.z.so)target_link_libraries(entry PUBLIC libnative_drawing.so)

同时在工程中的src/main/cpp/types/libentry/index.d.ts文件中,添加自定义绘制函数在ArkTS侧的定义,如:

import { DrawContext } from \'@kit.ArkUI\'export const nativeOnDraw: (id: number, context: DrawContext, width: number, height: number) => number;

ArkTS侧代码:

// Index.etsimport bridge from \"libentry.so\" // 该 so 由 Node-API 编写并生成import { DrawContext, FrameNode, NodeController, RenderNode } from \'@kit.ArkUI\'class MyRenderNode extends RenderNode { draw(context: DrawContext) { // 需要将 context 中的宽度和高度从vp转换为px bridge.nativeOnDraw(0, context, vp2px(context.size.height), vp2px(context.size.width)); }}class MyNodeController extends NodeController { private rootNode: FrameNode | null = null; makeNode(uiContext: UIContext): FrameNode | null { this.rootNode = new FrameNode(uiContext); const rootRenderNode = this.rootNode.getRenderNode(); if (rootRenderNode !== null) { const renderNode = new MyRenderNode(); renderNode.size = { width: 100, height: 100 } rootRenderNode.appendChild(renderNode); } return this.rootNode; }}@Entry@Componentstruct Index { private myNodeController: MyNodeController = new MyNodeController(); build() { Row() { NodeContainer(this.myNodeController) } }}