1 概述
FrameNode 是 ArkUI NG 中 "组件构建的 UI 节点,是组件树里的"实体节点"(有布局、有渲染、有事件),继承自:
AceType→Referenced→UINode→FrameNode- 同时继承
LayoutWrapper(让它可以直接参与 NG layout pipeline)
可以理解为:
UINode:只管"树结构 + 生命周期"(谁是父、谁是子、挂到哪棵树上)FrameNode:在此基础上,加上 布局、渲染、事件、可见性、Inspector 等完整 UI 能力- 源码位置 :
~/frameworks/core/components_ng/base/frame_node.h - 典型实例 :
Text,Button,Column等组件的节点,最终都落在不同类型的FrameNode上
实质上, 我们所有的前端目前所有的组件都会反馈在这里, 不论是arkui、 cangjieui 或者是腾讯Kuikly(KMP -> CAPI)。
2 类图

3 核心功能
3.1 节点核心行为(Pattern、 GeometryNode、LayoutProperty、 PaintProperty、RenderContext)

scss
FrameNode::FrameNode(
const std::string& tag, int32_t nodeId, const RefPtr<Pattern>& pattern, bool isRoot, bool isLayoutNode)
: UINode(tag, nodeId, isRoot), LayoutWrapper(WeakClaim(this)), pattern_(pattern)
{
isLayoutNode_ = isLayoutNode;
frameProxy_ = std::make_unique<FrameProxy>(this);
if (IsFree()) {
renderContext_->SetIsFree(IsFree());
renderContext_->SetHostNode(WeakClaim(this));
}
if (tag == V2::XCOMPONENT_ETS_TAG) {
renderContext_->InitContext(IsRootNode(), pattern_->GetContextParam(), isLayoutNode);
} else {
renderContext_->InitContext(IsRootNode(), pattern_->GetContextParam(), isLayoutNode, this);
}
paintProperty_ = pattern->CreatePaintProperty();
layoutProperty_ = pattern->CreateLayoutProperty();
// first create make layout property dirty.
layoutProperty_->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
layoutProperty_->SetHost(WeakClaim(this));
layoutSeperately_ = true;
paintProperty_->SetHost(WeakClaim(this));
// ...
}
Pattern 是最核心的部分,它决定了 FrameNode 持有什么类型的 LayoutProperty、PaintProperty、EventHub。不同组件(Text、Button、Column)只需要提供不同的 Pattern 子类,重写 CreateLayoutProperty() / CreatePaintProperty() 等工厂方法,就能让同一个 FrameNode 骨架承载不同的组件行为。
3.1.1 Pattern(行为逻辑层)
-
变量定义 :
RefPtr<Pattern> pattern_ -
职责:
- 定义组件的行为逻辑:如何响应属性变化、事件、生命周期回调等
- 创建 LayoutAlgorithm、PaintMethod、EventHub 等
- 类似 Flutter 的
RenderObject/ React 的组件逻辑层
-
关键方法:
csharp// 从 Pattern 获取各种组件 virtual RefPtr<LayoutProperty> CreateLayoutProperty(); virtual RefPtr<PaintProperty> CreatePaintProperty(); virtual RefPtr<LayoutAlgorithm> CreateLayoutAlgorithm(); virtual RefPtr<NodePaintMethod> CreateNodePaintMethod(); virtual RefPtr<EventHub> CreateEventHub(); // 生命周期回调 virtual void OnModifyDone(); virtual void OnAttachToFrameNode(); virtual void OnDetachFromFrameNode(); -
使用示例:
ini// 获取 Pattern 指针(带类型检查) auto* textPattern = frameNode->GetPatternPtr<TextPattern>(); // 获取 Pattern 的 RefPtr auto textPatternRef = frameNode->GetPattern<TextPattern>();
3.1.2 GeometryNode(几何信息层)
-
变量定义 :
RefPtr<GeometryNode> geometryNode_(通过继承 LayoutWrapper 获得) -
职责:
- 存储节点的几何信息:大小、位置、命中区域等
- 提供 frame rect 和 content rect 的访问接口
-
核心数据:
kotlinclass GeometryNode { private: SizeF frameSize_; // 节点整体大小 OffsetF frameOffset_; // 节点在父容器中的位置 SizeF contentSize_; // 内容区域大小(去除 padding) OffsetF contentOffset_; // 内容区域相对 frame 的偏移 RectF hitTestRect_; // 命中测试区域 }; -
使用场景:
scss// 获取节点大小 auto size = frameNode->GetGeometryNode()->GetFrameSize(); // 获取节点位置 auto offset = frameNode->GetGeometryNode()->GetFrameOffset(); // 获取内容区域 auto contentRect = frameNode->GetGeometryNode()->GetContentRect();
3.1.3 LayoutProperty(布局属性层)
-
变量定义 :
RefPtr<LayoutProperty> layoutProperty_ -
职责:
- 管理布局相关属性:margin/padding/flex/align 等
- 提供属性变更标记(PropertyChangeFlag)
- 存储 LayoutConstraint
-
核心属性:
arduinoclass LayoutProperty : public Property { private: std::optional<CalcSize> calcSize_; std::optional<CalcLength> calcLayoutConstraint_; std::optional<PaddingProperty> padding_; std::optional<MarginProperty> margin_; std::optional<FlexProperty> flexProperty_; std::optional<AlignDirection> align_; std::optional<VisibleType> visibility_; // ... 更多布局属性 }; -
使用示例:
scss// 更新布局属性 auto layoutProp = frameNode->GetLayoutPropertyPtr<LayoutProperty>(); layoutProp->UpdateMargin(MarginProperty { .left = 10.0f }); // 触发重新测量 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3.1.4 PaintProperty(绘制属性层)
-
变量定义 :
RefPtr<PaintProperty> paintProperty_ -
职责:
- 管理绘制相关属性:颜色、边框、阴影、透明度等
- 通过 RenderContext 间接使用
-
核心属性:
arduinoclass PaintProperty : public Property { private: std::optional<Color> backgroundColor_; std::optional<BorderWidthProperty> borderWidth_; std::optional<BorderColorProperty> borderColor_; std::optional<BorderRadiusProperty> borderRadius_; std::optional<Shadow> shadow_; std::optional<float> opacity_; // ... 更多绘制属性 };
3.1.5 RenderContext(渲染上下文层)
-
变量定义 :
RefPtr<RenderContext> renderContext_ -
职责:
- 封装 RSNode(RenderService Node)
- 管理绘制命令和渲染状态
- 控制真正的绘制节点
-
核心功能:
arduinoclass RenderContext { public: // 初始化 RSNode void InitContext(bool isRoot, std::optional<ContextParam> param); // 同步几何属性到 RSNode void SyncGeometryProperties(const RectF& rect, const RectF& contentRect); // 刷新绘制内容 void FlushContentModifier(const RefPtr<PaintWrapper>& wrapper); // 管理 RSNode 树 void AddChild(const RefPtr<RenderContext>& child, int32_t slot = -1); void RemoveChild(const RefPtr<RenderContext>& child); }; -
使用示例:
scss// 获取 RenderContext auto renderContext = frameNode->GetRenderContext(); // 设置背景色 renderContext->UpdateBackgroundColor(Color::RED); // 设置透明度 renderContext->UpdateOpacity(0.5f);
3.2 树结构与子节点(UINode进行了扩展)
在 UINode 的 children_ 基础上,FrameNode 额外维护了一套 按 ZIndex 排序的 Frame 子节点集合。
UINode::children_(结构树/逻辑树):
- 类型:std::list<RefPtr>
- 按 添加顺序 排列
- 包含 所有节点类型:FrameNode、SyntaxNode(ForEach、If 等语法节点)、CustomNode 等
- 用途:维护 DSL 声明的逻辑结构,做 diff、更新、遍历
FrameNode::frameChildren_(渲染树):
- 类型:std::multiset<WeakPtr, ZIndexComparator>
- 按 ZIndex 排序
- 只包含 FrameNode(过滤掉了所有语法节点)
- 用途:渲染绘制、命中测试
这个顺序不同会影响两个核心问题:
-
渲染顺序
RebuildRenderContextTree 按 frameChildren_ 的顺序重建 RSNode 树
scssvoid FrameNode::RebuildRenderContextTree() { // This function has a mirror function (XxxMultiThread) and needs to be modified synchronously. FREE_NODE_CHECK(this, RebuildRenderContextTree); if (!needSyncRenderTree_) { return; } auto pipeline = GetContextRefPtr(); if (pipeline && !pipeline->CheckThreadSafe()) { LOGW("RebuildRenderContextTree doesn't run on UI thread!"); } auto oldFrameChildren = std::move(frameChildren_); frameChildren_.clear(); std::list<RefPtr<FrameNode>> children; // generate full children list, including disappear children. GenerateOneDepthVisibleFrameWithTransition(children); if (overlayNode_) { auto property = overlayNode_->GetLayoutProperty(); if (property && property->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::VISIBLE) { children.push_back(overlayNode_); } } if (accessibilityFocusPaintNode_) { children.push_back(accessibilityFocusPaintNode_); } for (const auto& child : children) { frameChildren_.emplace(child); } // Notify AttachToRenderTree / DetachFromRenderTree. ProcessRenderTreeDiff(children, oldFrameChildren); oldFrameChildren.clear(); renderContext_->RebuildFrame(this, children); } -
命中测试顺序
-
TouchTest 按 frameChildren_ 反向遍历(ZIndex 从高到低),确保上层元素优先响应触摸
lessfor (auto iter = frameChildren_.rbegin(); iter != frameChildren_.rend(); ++iter) { if (GetHitTestMode() == HitTestMode::HTMBLOCK || GetHitTestMode() == HitTestMode::HTMBLOCK_DESCENDANTS) { break; }
-
3.3 标脏
脏标记系统是 FrameNode 驱动 NG Pipeline 的核心机制。当属性发生变化时,FrameNode 通过 MarkDirtyNode 将自身注册到 PipelineContext 的脏节点列表中,Pipeline 在下一帧统一处理这些脏节点,执行布局或渲染。
-
需要标记的内容
PropertyChangeFlag 是一组位标志,每个 bit 代表一种变更类型。按功能可分为三类:
测量类(影响节点尺寸,需要重新 Measure):
布局类(不影响尺寸,只影响位置):
渲染类(不影响布局,只影响绘制):
特殊标志:延迟标脏
-
标脏
scssvoid FrameNode::MarkDirtyNode(PropertyChangeFlag extraFlag) { // 分支1:冻结状态 → 只存 flag,不触发 Pipeline if (IsFreeze()) { layoutProperty_->UpdatePropertyChangeFlag(extraFlag); paintProperty_->UpdatePropertyChangeFlag(extraFlag); return; } // 分支2:PROPERTY_UPDATE_DIFF → 延迟标脏,加入 DirtyPropertyNode 列表 if (CheckNeedMakePropertyDiff(extraFlag)) { if (isPropertyDiffMarked_) return; context->AddDirtyPropertyNode(Claim(this)); isPropertyDiffMarked_ = true; return; } // 分支3:正常标脏 → 进入核心层 MarkDirtyNode(IsMeasureBoundary(), IsRenderBoundary(), extraFlag); }scss// frame_node.cpp:3107 void FrameNode::MarkDirtyNode(bool isMeasureBoundary, bool isRenderBoundary, PropertyChangeFlag extraFlag) { // 1. 将 flag 累积到 layoutProperty_ 和 paintProperty_ 上 layoutProperty_->UpdatePropertyChangeFlag(extraFlag); paintProperty_->UpdatePropertyChangeFlag(extraFlag); auto layoutFlag = layoutProperty_->GetPropertyChangeFlag(); auto paintFlag = paintProperty_->GetPropertyChangeFlag(); if (CheckNoChanged(layoutFlag | paintFlag)) return; // 2. 优先判断是否需要布局(布局优先级 > 渲染) if (CheckNeedRequestMeasureAndLayout(layoutFlag)) { // 2a. 非边界节点 → 向上传播给父节点 if (!isMeasureBoundary && IsNeedRequestParentMeasure()) { if (RequestParentDirty()) return; // 父节点接管,自己不入队 } // 2b. 到达边界 或 无父节点 → 自己入队 if (isLayoutDirtyMarked_) return; // 防重复 isLayoutDirtyMarked_ = true; context->AddDirtyLayoutNode(Claim(this)); return; } // 3. 不需要布局,只需要渲染 layoutProperty_->CleanDirty(); MarkNeedRender(isRenderBoundary); } -
什么时候中断:
- Scroll、List 等容器组件天然是测量边界 Scroll、List 等容器组件天然是测量边界
- 拥有独立渲染层的组件
- 是具有固定尺寸的节点------子节点尺寸变化不影响它