跳到主要内容

数据绑定

Rive 的 View Models 会暴露强类型属性(数字、字符串、颜色、布尔值、枚举、触发器、列表、嵌套视图模型、图像和画板引用),画板可以绑定到这些属性。在 C++ 中,你可以实例化一个视图模型、修改属性,绑定的视觉内容会在下一次 advanceAndApply 时更新。

概念

  • ViewModelRuntime — 在编辑器中定义的视图模型 schema。存在于 File 中。
  • ViewModelInstanceRuntime — 该 schema 实例的类型化包装器。暴露属性 API(propertyNumberpropertyString 等)。你通过 rcp<> 持有它。
  • ViewModelInstance — 底层可绑定实例。ArtboardStateMachineInstance 会绑定到这个类型。可通过 ViewModelInstanceRuntime.instance() 获取。
  • ViewModelInstance*Runtime — 各个属性的类型化句柄(…NumberRuntime…StringRuntime 等)。

创建实例

最简单的方式是向文件请求画板的默认视图模型,然后从中创建默认实例:

#include "rive/file.hpp"
#include "rive/viewmodel/runtime/viewmodel_runtime.hpp"

ViewModelRuntime* vm = file->defaultArtboardViewModel(artboard.get());
if (!vm) return; // artboard has no default view model

rcp<ViewModelInstanceRuntime> instance = vm->createDefaultInstance();
if (instance) {
artboard->bindViewModelInstance(instance->instance());
sm ->bindViewModelInstance(instance->instance());
}

如果需要完全控制,可以按索引或名称查找特定的视图模型 schema,并从中创建实例:

ViewModelRuntime* vm = file->viewModelByName("Card");
if (!vm) return; // no view model with that name

size_t propCount = vm->propertyCount();
size_t instCount = vm->instanceCount();

rcp<ViewModelInstanceRuntime> instance = vm->createDefaultInstance();
// alternatives — pick one and replace the line above:
// rcp<ViewModelInstanceRuntime> instance = vm->createInstanceFromName("Hero");
// rcp<ViewModelInstanceRuntime> instance = vm->createInstanceFromIndex(0);
// rcp<ViewModelInstanceRuntime> instance = vm->createInstance(); // no editor preset; properties at type defaults

if (!instance) return;
artboard->bindViewModelInstance(instance->instance());
sm ->bindViewModelInstance(instance->instance());

将同一个 ViewModelInstance 同时绑定到 画板状态机。 画板绑定会驱动影响布局的属性;状态机绑定会驱动状态机转场和监听器条件。

读取和写入属性

所有访问器都基于路径 — 嵌套视图模型使用 / 分隔。

auto* card = instance.get();

// Number
if (auto* score = card->propertyNumber("score")) {
score->value(42.0f);
float v = score->value();
}

// String
if (auto* title = card->propertyString("title")) {
title->value("Hello");
}

// Boolean
if (auto* on = card->propertyBoolean("isOpen")) {
on->value(true);
}

// Color (ARGB packed)
if (auto* col = card->propertyColor("accent")) {
col->value(0xFFE53935);
}

// Trigger (edge event)
if (auto* fire = card->propertyTrigger("fire")) {
fire->trigger();
}

// Enum (by string label)
if (auto* mood = card->propertyEnum("mood")) {
mood->value("happy");
}

嵌套视图模型

// Option 1: deep path string.
auto* headerTitle = card->propertyString("header/title");

// Option 2: walk the tree.
rcp<ViewModelInstanceRuntime> header = card->propertyViewModel("header");
auto* title = header->propertyString("title");

你也可以整体替换一个嵌套视图模型 — 这对于在不重建画板的情况下替换列表单元格的数据很有用:

rcp<ViewModelInstanceRuntime> newHeader = vm->createDefaultInstance();
card->replaceViewModel("header", newHeader.get());

列表

auto* items = card->propertyList("items");

// Append, insert, remove, replace, swap, count.
items->addInstance(rowInstance.get());
items->addInstanceAt(rowInstance.get(), 0);
items->removeInstanceAt(2);
items->swap(0, 1);
size_t n = items->size();

rcp<ViewModelInstanceRuntime> row = items->instanceAt(0);

列表项本身也是 ViewModelInstanceRuntime — 拥有与上面相同的属性 API。

图像和画板属性

auto* image = card->propertyImage("avatar");
image->value(decodedRenderImage.get()); // RenderImage*

auto* artboardRef = card->propertyArtboard("badge");
artboardRef->value(file->bindableArtboardNamed("Badge")); // rcp<BindableArtboard>

生命周期

  • ViewModelInstanceRuntimeViewModelInstance 的轻量包装器。只要还有对象绑定到它,就需要继续持有这个 rcp<>
  • 属性句柄(ViewModelInstanceNumberRuntime* 等)由父实例拥有。可以缓存该指针 — 它在实例生命周期内始终有效。
  • 修改属性后,下一次 sm->advanceAndApply(dt) 会通过数据绑定传播这些更改,并应用到渲染中。

当属性不存在时

每个 propertyX(name) getter 在名称无法解析时都会返回 nullptr,因此你可以安全地探测实例:

if (auto* p = instance->propertyNumber("optional")) {
p->value(1.0f);
}

如需内省,可以遍历 schema:

for (const PropertyData& p : instance->properties()) {
// p.name, p.type ∈ { number, string, boolean, color, enum, trigger,
// list, viewModel, image, artboard, ... }
}