数据绑定
Rive 的 View Models 会暴露强类型属性(number、string、color、boolean、enum、trigger、list、嵌套 view model、image 和 artboard 引用),artboard 可以绑定到这些属性。在 C++ 中,你可以实例化 view model、修改属性,并在下一次 advanceAndApply 时让绑定的视觉内容更新。
概念
ViewModelRuntime—— 编辑器中定义的 view model schema,存在于File中。ViewModelInstanceRuntime—— 对该 schema 实例的强类型包装,暴露属性 API(如propertyNumber、propertyString等)。通过rcp<>持有。ViewModelInstance—— 底层可绑定实例。Artboard和StateMachineInstance绑定到这个类型。可通过ViewModelInstanceRuntime的.instance()获取。ViewModelInstance*Runtime—— 单个属性的强类型 handle(如…NumberRuntime、…StringRuntime等)。
创建实例
最简单的路径是向文件请求 artboard 的默认 view model,然后创建其默认实例:
#include "rive/file.hpp"
#include "rive/viewmodel/runtime/viewmodel_runtime.hpp"
ViewModelRuntime* vm = file->defaultArtboardViewModel(artboard.get());
if (!vm) return; // artboard 没有默认 view model
rcp<ViewModelInstanceRuntime> instance = vm->createDefaultInstance();
if (instance) {
artboard->bindViewModelInstance(instance->instance());
sm ->bindViewModelInstance(instance->instance());
}
如果需要完全控制,可以按索引或名称查找特定 view model schema,并从中创建实例:
ViewModelRuntime* vm = file->viewModelByName("Card");
if (!vm) return; // 没有这个名称的 view model
size_t propCount = vm->propertyCount();
size_t instCount = vm->instanceCount();
rcp<ViewModelInstanceRuntime> instance = vm->createDefaultInstance();
// 其他可选方式:
// rcp<ViewModelInstanceRuntime> instance = vm->createInstanceFromName("Hero");
// rcp<ViewModelInstanceRuntime> instance = vm->createInstanceFromIndex(0);
// rcp<ViewModelInstanceRuntime> instance = vm->createInstance();
if (!instance) return;
artboard->bindViewModelInstance(instance->instance());
sm ->bindViewModelInstance(instance->instance());
备注
请将同一个 ViewModelInstance 同时绑定到 artboard 和 state machine。artboard 绑定驱动影响布局的属性,state-machine 绑定驱动状态机 transition 和 listener condition。
读取与写入属性
所有访问器都基于路径,嵌套 view model 使用 / 分隔。
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(按字符串 label)
if (auto* mood = card->propertyEnum("mood")) {
mood->value("happy");
}
嵌套 View Models
// 方式 1:深层路径字符串。
auto* headerTitle = card->propertyString("header/title");
// 方式 2:逐层遍历。
rcp<ViewModelInstanceRuntime> header = card->propertyViewModel("header");
auto* title = header->propertyString("title");
你也可以整体替换嵌套 view model,这对不重建 artboard 就交换列表单元格数据很有用:
rcp<ViewModelInstanceRuntime> newHeader = vm->createDefaultInstance();
card->replaceViewModel("header", newHeader.get());
Lists
auto* items = card->propertyList("items");
// 追加、插入、删除、替换、交换、计数。
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);
List item 本身也是 ViewModelInstanceRuntime,可使用相同属性 API。
Image 与 Artboard 属性
auto* image = card->propertyImage("avatar");
image->value(decodedRenderImage.get()); // RenderImage*
auto* artboardRef = card->propertyArtboard("badge");
artboardRef->value(file->bindableArtboardNamed("Badge")); // rcp<BindableArtboard>
生命周期
ViewModelInstanceRuntime是ViewModelInstance之上的轻量包装。只要有对象绑定到它,就要保持rcp<>存活。- 属性 handle(如
ViewModelInstanceNumberRuntime*)由父 instance 拥有。可以缓存指针,它会在 instance 生命周期内保持有效。 - 修改属性后,下一次
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, ... }
}