ACE Engine 属性更新流程通用规则
文档版本: v1.0 创建时间: 2026-02-02 适用范围: ACE Engine NG 组件开发
目录
核心概念
属性更新标志 (Property Update Flags)
在 ACE Engine NG 架构中,每个属性在定义时都必须指定更新标志,用于决定属性变化后触发的 pipeline 阶段。
// 位置: core/components_ng/property/property.h
enum PropertyUpdateType {
PROPERTY_UPDATE_MEASURE = 0, // 触发布局测量
PROPERTY_UPDATE_LAYOUT = 1, // 触发布局定位
PROPERTY_UPDATE_RENDER = 2, // 直接触发渲染
PROPERTY_UPDATE_MEASURE_SELF = 3, // 仅测量自身
};
三大 Pipeline 阶段
ArkTS 属性设置
↓
Model 层静态方法
↓
PaintProperty 更新 (标记脏标记)
↓
[根据 PropertyUpdateType 分流]
├→ PROPERTY_UPDATE_MEASURE → Measure → Layout → Render
├→ PROPERTY_UPDATE_LAYOUT → Layout → Render
└→ PROPERTY_UPDATE_RENDER → Render (直接渲染)
↓
Modifier/Drawing 渲染
属性更新标志详解
PROPERTY_UPDATE_MEASURE
触发阶段: Measure → Layout → Render
适用场景:
- 属性变化会影响组件的尺寸计算
- 需要重新测量内容大小
- 可能影响子组件的布局
典型属性:
Value(Progress 组件) - 进度值变化可能影响文本尺寸Color(Progress 组件) - 颜色变化可能影响布局(某些复杂场景)StrokeWidth- 描边宽度直接影响尺寸Max- 最大值变化影响整体比例
定义示例:
// progress_paint_property.h:74
ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(Value, double, PROPERTY_UPDATE_MEASURE);
// progress_paint_property.h:91
ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(Color, Color, PROPERTY_UPDATE_MEASURE);
PROPERTY_UPDATE_RENDER
触发阶段: Render (直接渲染)
适用场景:
- 纯视觉属性变化
- 不影响尺寸和布局
- 仅需要重绘
典型属性:
ProgressStatus- 进度状态(进行中/加载中)EnableSmoothEffect- 平滑特效开关IsSensitive- 隐私模式标志PaintShadow- 阴影绘制开关
定义示例:
// progress_paint_property.h:98
ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(PaintShadow, bool, PROPERTY_UPDATE_RENDER);
// progress_paint_property.h:99
ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ProgressStatus, ProgressStatus, PROPERTY_UPDATE_RENDER);
PROPERTY_UPDATE_LAYOUT
触发阶段: Layout → Render
适用场景:
- 属性变化不影响组件自身尺寸
- 但可能影响子组件或定位
- 需要重新计算位置
典型属性:
Alignment- 对齐方式Padding- 内边距(某些实现)
PROPERTY_UPDATE_MEASURE_SELF
触发阶段: 仅测量自身 → Layout → Render
适用场景:
- 仅影响自身测量,不影响父组件
标准更新流程
完整流程图
┌─────────────────────────────────────────────────────────────────┐
│ ArkTS 应用层 │
│ component.property(newValue) │
└────────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ ModelNG 层 (静态方法) │
│ ProgressModelNG::SetValue(FrameNode*, value) │
│ ↓ │
│ auto paintProperty = frameNode->GetPaintProperty(); │
│ paintProperty->SetValue(value); │
└────────────────────────────┬────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ PaintProperty 层 (属性存储) │
│ ACE_UPDATE_PAINT_PROPERTY(ProgressPaintProperty, Value, val) │
│ ↓ │
│ propValue_ = value; │
│ MarkDirty(PROPERTY_UPDATE_MEASURE); ← 关键:标记脏标记 │
└────────────────────────────┬────────────────────────────────────┘
↓
┌──────────────┴──────────────┐
│ 检查 PropertyUpdateType │
└──────────────┬──────────────┘
↓
┌────────────────────┴────────────────────┐
│ │
[MEASURE] [RENDER]
│ │
↓ ↓
┌───────────────┐ ┌───────────────┐
│ OnModifyDone() │ ← 统一入口 │ OnModifyDone() │
└───────┬───────┘ └───────┬───────┘
│ │
↓ ↓
┌───────────────┐ ┌───────────────┐
│ Measure │ ← 布局算法 │ (跳过) │
│ Layout │ ← 位置计算 │ │
└───────┬───────┘ └───────┬───────┘
│ │
└──────────────────┬───────────────────────┘
↓
┌──────────────────────────────────────┐
│ PaintMethod 层 │
│ UpdateContentModifier(paintWrapper) │
└──────────────────┬───────────────────┘
↓
┌──────────────────────────────────────┐
│ Modifier 层 │
│ modifier->SetValue(value) │
│ modifier->SetColor(color) │
└──────────────────┬───────────────────┘
↓
┌──────────────────────────────────────┐
│ 渲染层 (Drawing) │
│ onDraw(DrawingContext&) │
└──────────────────────────────────────┘
关键入口:OnModifyDone()
所有属性更新最终都会汇聚到 Pattern::OnModifyDone(),这是统一的生命周期入口:
// progress_pattern.cpp
void ProgressPattern::OnModifyDone()
{
// 1. 初始化各种事件监听器(仅首次)
InitFocusEvent();
InitTouchEvent();
InitHoverEvent();
// 2. 获取最新的属性值
auto paintProperty = GetPaintProperty<ProgressPaintProperty>();
auto value = paintProperty->GetValue().value_or(0.0);
// 3. 准备 Modifier
if (!progressModifier_) {
progressModifier_ = AceType::MakeRefPtr<ProgressModifier>(...);
}
// 4. 注意:这里不直接设置属性到 Modifier
// 而是标记需要更新,真正的更新在 PaintMethod::UpdateContentModifier 中
}
真正的属性传递:PaintMethod::UpdateContentModifier()
// progress_paint_method.cpp
void ProgressPaintMethod::UpdateContentModifier(PaintWrapper* paintWrapper)
{
// 1. 从 PaintWrapper 获取 PaintProperty(此时包含最新值)
auto paintProperty = DynamicCast<ProgressPaintProperty>(paintWrapper->GetPaintProperty());
// 2. 提取属性值
value_ = paintProperty->GetValue().value_or(value_);
color_ = paintProperty->GetColor().value_or(color_);
maxValue_ = paintProperty->GetMaxValue().value_or(maxValue_);
// 3. 计算相关值(如果有)
CalculateStrokeWidth(paintWrapper->GetContentSize());
// 4. 设置到 Modifier(真正的数据传递)
progressModifier_->SetValue(value_);
progressModifier_->SetColor(LinearColor(color_));
progressModifier_->SetMaxValue(maxValue_);
}
实际案例分析
案例 1: Progress 组件的 Value 更新
属性定义:
// progress_paint_property.h:74
ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(Value, double, PROPERTY_UPDATE_MEASURE);
完整流程:
// 1. ArkTS 层
progress.value(75)
// 2. ModelNG 层 (progress_model_ng.cpp:85)
void ProgressModelNG::SetValue(FrameNode* frameNode, double value)
{
auto paintProperty = frameNode->GetPaintProperty<ProgressPaintProperty>();
paintProperty->SetValue(value); // 触发 PROPERTY_UPDATE_MEASURE
}
// 3. PaintProperty 层
// SetValue() 内部:
// propValue_ = value;
// MarkDirty(PROPERTY_UPDATE_MEASURE); ← 标记需要重新测量
// 4. Pipeline 调度
// 由于是 PROPERTY_UPDATE_MEASURE,触发:
// OnModifyDone() → Measure → Layout → Render
// 5. PaintMethod 层 (progress_paint_method.cpp:112)
void ProgressPaintMethod::UpdateContentModifier(PaintWrapper* paintWrapper)
{
// 此时 PaintProperty 包含最新的 value
value_ = paintProperty->GetValue().value_or(value_);
// 设置到 Modifier
progressModifier_->SetMaxValue(maxValue_);
progressModifier_->SetValue(isSensitive ? 0.0 : value_);
}
// 6. Modifier 渲染 (progress_modifier.cpp)
void ProgressModifier::SetValue(float value)
{
value_->Set(value); // AnimatableProperty 触发动画
}
案例 2: Progress 组件的 Color 更新
属性定义:
// progress_paint_property.h:91
ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(Color, Color, PROPERTY_UPDATE_MEASURE);
为什么 Color 也是 MEASURE?
虽然颜色通常是纯视觉属性,但在 Progress 组件中,颜色变化可能需要:
- 重新计算某些与颜色相关的布局参数
- 触发
ProgressPattern::UpdateColor()中的主题相关逻辑 - 在某些复杂场景下(如渐变色),可能影响测量
完整流程:
// 1. ArkTS 层
progress.color(Color.Red)
// 2. ModelNG 层 (progress_model_ng.cpp:86)
void ProgressModelNG::SetColor(FrameNode* frameNode, const Color& value)
{
auto paintProperty = frameNode->GetPaintProperty<ProgressPaintProperty>();
paintProperty->SetColor(value); // 触发 PROPERTY_UPDATE_MEASURE
}
// 3. Pattern 层 - 特殊处理
// 在 OnModifyDone() 中可能调用:
void ProgressPattern::UpdateColor(const Color& color, bool isFirstLoad)
{
// 1. 检查是否用户主动设置
isUserInitiatedColor_ = true;
// 2. 颜色模式相关处理
// 3. 主题相关处理
}
// 4. PaintMethod 层 (progress_paint_method.cpp:64)
void ProgressPaintMethod::UpdateContentModifier(PaintWrapper* paintWrapper)
{
// SCALE 类型特殊处理
if (progressType_ != ProgressType::SCALE) {
color_ = isUserInitiatedColor_ ? paintProperty->GetColor().value_or(color_) : color_;
} else {
color_ = paintProperty->GetColor().value_or(color_);
}
// 设置到 Modifier
progressModifier_->SetColor(LinearColor(color_));
}
案例 3: ProgressStatus 更新 (RENDER 类型)
属性定义:
// progress_paint_property.h:99
ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ProgressStatus, ProgressStatus, PROPERTY_UPDATE_RENDER);
完整流程:
// 1. ArkTS 层
progress.progressStatus(ProgressStatus.LOADING)
// 2. ModelNG 层 (progress_model_ng.cpp:93)
void ProgressModelNG::SetProgressStatus(FrameNode* frameNode, ProgressStatus status)
{
auto paintProperty = frameNode->GetPaintProperty<ProgressPaintProperty>();
paintProperty->SetProgressStatus(status); // 触发 PROPERTY_UPDATE_RENDER
}
// 3. Pipeline 调度
// 由于是 PROPERTY_UPDATE_RENDER,跳过 Measure 和 Layout,直接:
// OnModifyDone() → Render
// 4. PaintMethod 层
void ProgressPaintMethod::UpdateContentModifier(PaintWrapper* paintWrapper)
{
ProgressStatus progressStatus = paintProperty->GetProgressStatus()
.value_or(ProgressStatus::PROGRESSING);
progressModifier_->SetProgressStatus(progressStatus);
}
// 5. Modifier 渲染
void ProgressModifier::SetProgressStatus(ProgressStatus status)
{
progressStatus_->Set(static_cast<int>(status));
// 可能启动或停止加载动画
}
常见问题与实践指南
Q1: 为什么很多"视觉属性"定义为 MEASURE 而不是 RENDER?
原因:
- 历史设计: NG 架构早期,很多属性为了保守起见使用了 MEASURE
- 复杂交互: 某些属性在特定场景下确实影响布局(如带文本的进度条)
- 性能权衡: MEASURE 虽然更重,但能确保所有场景正确性
实践指南:
- 新组件开发时,优先考虑使用
PROPERTY_UPDATE_RENDER - 只有在确认影响测量或布局时才使用
PROPERTY_UPDATE_MEASURE - 可以通过性能测试来决定使用哪个标志
Q2: 如何快速判断属性应该使用哪个标志?
决策树:
属性变化后...
│
├→ 是否影响组件自身的 width/height?
│ ├→ 是 → PROPERTY_UPDATE_MEASURE
│ └→ 否 ↓
├→ 是否影响子组件的布局或位置?
│ ├→ 是 → PROPERTY_UPDATE_LAYOUT
│ └→ 否 ↓
└→ 仅影响视觉效果(颜色、阴影等)?
└→ 是 → PROPERTY_UPDATE_RENDER
常见属性分类:
| 属性类型 | 典型例子 | 推荐标志 |
|---|---|---|
| 尺寸相关 | width, height, padding | MEASURE |
| 内容相关 | value, text, data | MEASURE |
| 位置相关 | margin, offset | LAYOUT |
| 纯视觉 | color, shadow, opacity | RENDER |
| 状态相关 | enabled, selected | RENDER |
Q3: OnModifyDone() 中可以做哪些操作?
可以做:
- 初始化事件监听器(仅首次)
- 获取最新的属性值并缓存
- 创建和配置 Modifier
- 触发一次性初始化操作
不应该做:
- 直接修改 Modifier 的属性值(应该在 PaintMethod 中做)
- 触发异步操作(可能导致不一致)
- 长时间计算(影响性能)
Q4: PaintMethod 和 Pattern 的职责如何划分?
Pattern 职责:
- 生命周期管理
- 事件监听器初始化
- 创建和配置 PaintMethod、Modifier
- 处理特殊的业务逻辑(如颜色模式切换)
PaintMethod 职责:
- 从 PaintProperty 提取最新属性值
- 计算派生值(如根据内容尺寸计算 stroke width)
- 将值设置到 Modifier
- 是 Property → Modifier 的真正桥梁
Modifier 职责:
- 存储动画属性值
- 实现 onDraw() 绘制逻辑
- 处理动画和特效
Q5: 如何追踪属性更新流程?
调试技巧:
- 查看属性定义:
# 在组件的 paint_property.h 中查找属性定义
grep "ACE_DEFINE_PROPERTY_ITEM.*Value" progress_paint_property.h
# 输出: ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(Value, double, PROPERTY_UPDATE_MEASURE);
- 追踪 MarkDirty 调用:
// 在 PaintProperty 基类中
template<typename T>
void SetProperty(const T& value) {
if (propValue_ != value) {
propValue_ = value;
MarkDirty(PROPERTY_UPDATE_MEASURE); // 断点这里
}
}
- 查看 Pipeline 调度日志:
# 运行时启用日志
export ACE_LOG_COMPONENT=progress
# 查看流程: OnModifyDone → Measure → Layout → Render
附录:常用宏定义说明
ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP
// 定义一个独立的属性
ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(Value, double, PROPERTY_UPDATE_MEASURE);
// 展开后的代码结构:
class ProgressPaintProperty {
std::optional<double> propValue_;
bool HasValue() const { return propValue_.has_value(); }
const std::optional<double>& GetValue() const { return propValue_; }
void SetValue(const double& value) {
propValue_ = value;
MarkDirty(PROPERTY_UPDATE_MEASURE); // 自动标记脏
}
void ResetValue() { propValue_.reset(); }
std::optional<double> CloneValue() const { return propValue_; }
};
ACE_DEFINE_PROPERTY_ITEM_WITH_GROUP
// 定义一个分组属性
ACE_DEFINE_PROPERTY_ITEM_WITH_GROUP(ProgressPaintDate, Value, double, PROPERTY_UPDATE_MEASURE);
// 展开后的代码结构:
class ProgressPaintProperty {
struct ProgressPaintDateGroup {
std::optional<double> propValue_;
// ... 其他分组属性
};
ProgressPaintDateGroup propProgressPaintDate_;
const std::optional<double>& GetValue() const {
return propProgressPaintDate_.propValue_;
}
};
总结
核心要点
-
属性定义决定流程:
PROPERTY_UPDATE_MEASUREvsPROPERTY_UPDATE_RENDER决定了属性变化后是否经过 Layout 阶段 -
统一入口 OnModifyDone(): 所有属性更新都会经过此方法,但真正的属性传递在
PaintMethod::UpdateContentModifier() -
三层分离: Pattern(生命周期)→ PaintMethod(属性转换)→ Modifier(渲染)
-
MEASURE 不等于慢: 虽然经过更多阶段,但能确保正确性。对于简单视觉属性,优先使用 RENDER
快速参考
| 场景 | 使用标志 | 是否经过 Layout |
|---|---|---|
| 进度值变化 | MEASURE | ✓ |
| 颜色变化 | MEASURE | ✓ |
| 状态切换 | RENDER | ✗ |
| 尺寸变化 | MEASURE | ✓ |
| 纯视觉特效 | RENDER | ✗ |
相关文档: