跳到主要内容

从旧版 Rive Android 运行时迁移

概述​

新的 Rive Android 运行时是对公共 API 和内部架构的近乎完全的重写。虽然在概念上大多数操作都有等效项,但两个 API 不兼容。你在旧版 API 中任何想迁移的现有工作都必须使用新的 API 重新构建。本指南旨在演示如何迁移每个功能区域。

本指南涵盖:

  1. 共享功能 - 常见操作及其等效项。
  2. 新的独占功能 - 仅在新运行时中可用的功能。
  3. 旧版独占功能 - 新运行时不再支持的功能。大多数都有变通方法或替代方案;这些移除简化了 API 表面,减少了维护开销,并消除了反模式。

本指南并非详尽无遗,因为那样会与现有的通用文档重复。有关特定主题的更多详细信息,请参阅文档的相关部分。

包​

旧版 Rive Android 运行时位于 app.rive.runtime.kotlin 包(+ .core)中。新运行时位于 app.rive 包(+ .core)中。这允许同名类(如 Artboard)共存而不冲突。如果你需要在同一文件中使用两个包中的同名类,可以使用 Kotlin 的 import as 功能为其中一个导入添加别名。

import app.rive.Artboard
import app.rive.runtime.kotlin.core.Artboard as LegacyArtboard

异步 API​

由于基于 RiveWorker 的新线程模型,几乎所有在主线程上原本同步返回值的操作现在都是异步的 suspend 函数,例如加载 Rive 文件和执行查询。你需要从协程上下文中调用这些函数,例如在 Compose 的 LaunchedEffect 中,或使用 Activity 或 Fragment 中的 lifecycleScope.launch。

最常见的 Compose 操作被包装在辅助函数中,例如 rememberRiveFile,它将异步状态公开为 Result 类型以便在可组合项中更易于使用。其他操作是"即发即弃"的,例如从加载的 Rive 文件创建画板和视图模型实例,或推进状态机。

生命周期​

旧版 Rive Android 运行时基于 View,RiveAnimationView 类根据所属 Activity 或 Fragment 的生命周期管理其生命周期。Rive 文件的生命周期与视图的生命周期相关联,渲染器、控制器以及创建的对象(如画板和状态机)也是如此。这些使用分层引用计数来管理其生命周期。

新运行时使用 AutoCloseable 接口来显式管理对象(如 Rive 文件、画板和状态机)的生命周期。相比之下,worker 使用引用计数,并拥有其所创建资源的所有内存。手动创建对象时,你必须 close 它们以释放其资源,否则它们将在 worker 的生命周期内持续存在。或者,你可以将它们与 use 辅助函数一起使用,该函数将在其块末尾调用 close。worker 本身必须释放所有引用,否则它及其资源将会泄漏。

在 Compose 中,生命周期与 remember 可组合项绑定,并使用 DisposableEffect 根据需要创建和处置对象,简化了内存管理。

共享功能​

共享的 API​

一些 API 在旧版和新运行时之间是共享的。它们仍然位于旧版运行时的 app.rive.runtime.kotlin 包中。

  • 用于初始化的 Rive 全局对象,包括 Rive.init 方法。
  • RiveInitializer XML 初始化辅助工具。
  • 回退字体 API,包括 FontFallbackStrategy 接口和 FontFallbackStrategy.stylePicker 全局变量。

这些共享的 API 将来可能会发生变化,变为在新包中具有修订版本的完全分离。这只会在主要版本发布时作为破坏性变更发生。

RiveAnimationView 到 Rive​

旧版 Rive Android 运行时基于 View,提供了 RiveAnimationView 类来显示 Rive 动画。该名称反映了其创建时较有限的功能集,主要用于动画播放。

新 API 基于 Compose,计划将来提供 View API。主要的可组合项简称为 Rive,反映了其支持的更广泛的功能集和用例。

设置本地原始资源​

新 API 将默认使用 I/O 调度器异步加载 Rive 文件。这与旧版运行时不同,旧版运行时在主线程上同步加载文件。如果你想使用不同的线程模型加载文件,请使用你首选的方法加载资源,并将字节传递给 RiveFileSource.Bytes 源。

旧版 RiveAnimationView

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.build()

// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file)

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file" />

新 Rive 可组合项

val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)

when (riveFile) {
is Result.Success -> Rive(riveFile.value)
else -> {} // 适当处理加载和错误状态
}

从字节设置 Rive 文件​

旧版 RiveAnimationView

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(myBytes)
.build()

// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveBytes(myBytes)

新 Rive 可组合项

val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.Bytes(myBytes),
riveWorker
)

when (riveFile) {
is Result.Success -> Rive(riveFile.value)
else -> {} // 适当处理加载和错误状态
}

缓存 Rive 文件​

有关更多详细信息,另请参阅缓存 Rive 文件。

在新运行时中,Rive 文件始终是显式创建的,而不是通常由视图隐式创建。这使得在多个 Rive 实例之间缓存和重用 Rive 文件更加明显。

旧版 RiveAnimationView

// 为简单起见在主线程加载字节;在生产环境中考虑在后台线程加载。
val bytes = resources.openRawResource(R.raw.my_rive_file).use { res -> res.readBytes() }
val riveFile = File(bytes)

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(riveFile)
.build()

// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveFile(riveFile)

// 如果不再需要该文件则释放,如果计划重用它则保留。
riveFile.release()

新 Rive 可组合项

由 rememberRiveFile 生成的 Rive 文件可以被提升到更高级别的可组合项,以存活更长时间并在多个 Rive 实例之间共享。

val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)

when (riveFile) {
is Result.Success -> Rive(riveFile.value)
else -> {} // 适当处理加载和错误状态
}

选择画板和状态机​

有关更多详细信息,另请参阅画板文档和状态机文档。

旧版 RiveAnimationView

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setArtboardName("My Artboard")
.setStateMachineName("My State Machine")
.build()

// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file,
artboardName = "My Artboard",
stateMachineName = "My State Machine"
)

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveArtboard="My Artboard"
app:riveStateMachine="My State Machine" />

新 Rive 可组合项

画板和状态机作为对象而不是名称传递。你可以使用 rememberArtboard 和 rememberStateMachine 辅助函数从加载的 RiveFile 获取这些对象。

val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)

when (riveFile) {
is Result.Success -> {
val artboard = rememberArtboard(riveFile.value, "My Artboard")
val stateMachine = rememberStateMachine(artboard, "My State Machine")

Rive(
riveFile.value,
artboard = artboard,
stateMachine = stateMachine
)
}
else -> {} // 适当处理加载和错误状态
}

设置适配和对齐​

有关更多详细信息,另请参阅布局文档。

旧版 RiveAnimationView

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setFit(Fit.CONTAIN)
.setAlignment(Alignment.CENTER)
.build()

// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file,
fit = Fit.CONTAIN,
alignment = Alignment.CENTER
)

// 属性
riveView.controller.fit = Fit.CONTAIN
riveView.controller.alignment = Alignment.CENTER

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveFit="CONTAIN"
app:riveAlignment="CENTER" />

新 Rive 可组合项

新运行时将适配模式建模为密封类,仅在支持对齐的变体上允许设置对齐。此外,Layout 变体公开了布局缩放因子。

Rive(
riveFile,
fit = Fit.Contain(Alignment.Center),
)

自动绑定​

有关更多详细信息,另请参阅数据绑定中的自动绑定部分。

旧版 RiveAnimationView

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAutoBind(true)
.build()

// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file, autoBind = true)

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveAutoBind="true" />

新 Rive 可组合项

由于可组合项的性质,Compose API 中没有实现自动绑定。由于它们是函数,与类相比很难从中获取值。回调需要一个空占位符来在触发前记住值,这比直接提供实例产生更多开销。

等效做法是创建一个无源的视图模型实例。这将在内部创建默认画板、该画板的默认视图模型以及该视图模型的默认实例。然后可以将其传递给 Rive 可组合项。

val vmi = rememberViewModelInstance(riveFile)
Rive(
riveFile,
viewModelInstance = vmi,
)

播放、暂停和状态机稳定​

旧版 RiveAnimationView

旧版 API 在 RiveAnimationView 类上包含 play() 和 pause() 方法来控制动画的播放。值得注意的是,暂停状态与稳定状态相同,这并不理想。play() 将同时取消暂停和取消稳定状态机,使其推进。

riveView.play()  // 取消稳定并播放
riveView.pause() // 暂停

新 Rive 可组合项

在新运行时中,没有 play 或 pause 方法。相反,播放状态的控制通过 Rive 可组合项上的 playing 参数完成。将 playing 设置为 false 将暂停动画,而将其设置为 true 将恢复播放。状态机的稳定和取消稳定是自动完成的,涵盖了所有需要它的场景。

var isPlaying by remember { mutableStateOf(true) }
Rive(
riveFile,
playing = isPlaying
)

自动播放和设置初始值​

旧版运行时包含一个 autoplay 功能(默认为 true),在加载 Rive 文件时自动开始播放给定的动画或状态机。可以禁用它以在播放开始前设置初始值。

旧版 RiveAnimationView

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAutoPlay(true)
.build()

// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file, autoPlay = true)

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveAutoPlay="true" />

新运行时没有包含 autoplay 功能,因为它不是必需的——Rive 文件与其展示是解耦的。你可以将 playing 保留为其默认值 true 以实现类似行为。要在播放开始前设置初始值,可以将 playing 设置为 false,更新数据绑定的属性,然后将 playing 设置为 true 开始播放。

var isPlaying by remember { mutableStateOf(false) }
val vmi = rememberViewModelInstance(riveFile)

LaunchedEffect(vmi) {
// 设置初始值
vmi.setNumber("My Number Property", 42.0f)

// 开始播放
isPlaying = true
}

Rive(
riveFile,
playing = isPlaying,
viewModelInstance = vmi,
)

触摸穿透​

旧版 RiveAnimationView

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setTouchPassThrough(true)
.build()

// 属性
riveView.touchPassThrough = true

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveTouchPassThrough="true" />

新 Rive 可组合项

新运行时在 Rive 可组合项上提供了 touchPassThrough 参数。这更加细致,可以设置为 Consume(默认行为)、Observe 或 PassThrough。Observe 仅与 Compose 层次结构中的祖先共享,适用于不阻止滚动事件,而 PassThrough 还与同级共享(等效于旧版行为)。

Rive(
riveFile,
touchPassThrough = RivePointerInputMode.PassThrough,
)

视图模型​

旧版

旧版运行时包含一个从 File 检索的 ViewModel 类,对应于 Rive 编辑器中的视图模型。此类提供了查询和创建视图模型实例的方法。

val viewModel = riveFile.getViewModelByName("My View Model")
val instance = viewModel.createInstanceFromName("My Instance")

val properties = viewModel.properties

新版

新运行时将查询合并到 RiveFile 类中,在查询需要时包含视图模型名称。视图模型实例由 ViewModelSource 指定,可以是 Named 或 DefaultForArtboard。然后,该 ViewModelSource 可以使用 blankInstance、defaultInstance 或 namedInstance 辅助函数转换为 ViewModelInstanceSource。完成的源可以传递给 rememberViewModelInstance 或 ViewModelInstance.fromFile。

val instanceSource = ViewModelSource.Named("My View Model").namedInstance("My Instance")
val instance = rememberViewModelInstance(riveFile, instanceSource)

LaunchedEffect(riveFile) {
val properties = riveFile.getViewModelProperties("My View Model")
}

视图模型实例属性​

旧版

旧版运行时在 ViewModelInstance 类上提供显式的属性对象,用于查询和更新数据绑定属性。

// 获取属性
val numberProperty = vmi.getNumberProperty("My Number Property")
// 读取其值
val numberValue = numberProperty.value
// 观察其值
numberProperty.valueFlow.collect { value ->
// 处理值更新
}
// 写入其值
numberProperty.value = 42.0f

新版

新运行时不提供显式的属性对象。相反,属性通过 getter 和 setter 方法按名称在 ViewModelInstance 上查询和更新。

LaunchedEffect(vmi) {
// 读取属性值
val numberValue = vmi.getNumberFlow("My Number Property").first()
// 观察其值
vmi.getNumberFlow("My Number Property").collect { value ->
// 处理值更新
}
}

// 在 Compose 中观察
val numberValue by vmi.collectNumberAsState("My Number Property", initial = 0.0f)
// 写入属性值
vmi.setNumber("My Number Property", 42.0f)

可绑定画板​

旧版

旧版运行时包含一个 BindableArtboard 类,该类包装了原生 Artboard 并提供数据绑定功能。此类旨在限制 API 表面并通过限制对底层画板的直接访问来防止反模式。

val bindableArtboard = riveFile.createBindableArtboardByName("My Artboard")
val artboardProperty = vmi.getArtboardProperty("My Artboard Property")
artboardProperty.set(bindableArtboard)

新版

新运行时没有包含 BindableArtboard 类。相反,两种情况使用相同的 Artboard 类。

val artboard = rememberArtboard(riveFile, "My Artboard")
vmi.setArtboard("My Artboard Property", artboard)

加载引用资源​

旧版

旧版运行时使用"拉取"模型来加载引用资源,运行时通过回调按需请求资源(通过 FileAssetLoader 类或其子类)。这允许延迟加载资源,但移除了用户对资源何时加载的控制。

旧版 RiveAnimationView

class MyAssetLoader(private val context: Context) : ContextAssetLoader(context) {
// 处理一个名为 "my_image" 的图片资源的示例资源加载器
override fun loadContents(asset: FileAsset, inBandBytes: ByteArray): Boolean {
val identifier =
if (asset.uniqueFilename == "my_image") R.raw.my_image
else return false

context.resources.openRawResource(identifier).use {
asset.decode(it.readBytes())
}
return true
}
}

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAssetLoader(MyAssetLoader(context))
.build()

// Set 方法
val riveView = RiveAnimationView(context)
riveView.setAssetLoader(MyAssetLoader(context))
riveView.setRiveResource(R.raw.my_rive_file)

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveAssetLoaderClass="com.example.MyAssetLoader" />

新版

新运行时使用"推送"模型,应用程序负责在需要之前将引用资源提供给 worker。如果在需要之后才提供资源,它们将"弹出"显示。此更改旨在简化内部架构,并允许用户灵活地以他们选择的任何方式加载资源。

目前获取所需资源列表的方法是通过检查导出的 .zip 中的文件。将来我们将添加一个 API 来从 Rive 文件中查询所需资源的清单。

资源对 worker 是"全局的",这意味着它们可以在多个 Rive 文件和实例之间共享,只要它们具有相同的键。这减少了常见资源(如字体)的内存使用。

val riveWorker = rememberRiveWorker()
val bytesResult = produceState<Result<ByteArray>>(Result.Loading) {
value = withContext(Dispatchers.IO) {
context.resources.openRawResource(R.raw.my_image)
.use { Result.Success(it.readBytes()) }
}
}.value
val image = bytesResult.andThen { bytes -> rememberRegisteredImage(riveWorker, "My Image", bytes) }

默认布局缩放因子​

旧版运行时使用报告的设备密度作为以 Layout 作为适配模式渲染 Rive 时的默认缩放因子。

新运行时使用默认缩放因子为 1.0,意味着 Rive 单位直接映射到屏幕像素。在实践中,匹配密度通常会导致视觉效果过大。但是,如果这是期望的行为并且你想匹配旧版运行时,可以使用 LocalDensity 将缩放因子设置为设备密度。

val density = LocalDensity.current.density
Rive(
riveFile,
fit = Fit.Layout(scaleFactor = density)
)

新的独占功能​

越来越多的功能仅在新运行时中可用。这些功能是为了解决旧版运行时的限制或提供以前不可能的新功能而添加的。以下是这些功能的列表及简要说明。

日志记录​

新运行时包含一个细粒度、灵活的日志记录系统,允许你在不同级别(debug、info、warning、error)捕获日志,并将其重定向到你偏好的日志框架或接收器。

相比之下,旧版运行时的日志记录有限。

我们将来可能会考虑为旧版运行时改造更多日志记录能力,特别是如果这样做能揭示潜在的错误来源。

跟踪 Rive 文件的加载状态​

新运行时提供了一个围绕 Rive 文件加载操作的 Result 包装器,允许你跟踪 Rive 文件的状态(加载中、成功、错误)。这对于在加载期间提供用户反馈或妥善处理错误非常有用。

val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)
when (riveFile) {
is Result.Loading -> {
// 显示加载指示器
}
is Result.Failure -> {
// 显示错误消息
}
is Result.Success -> {
Rive(riveFile.value)
}
}

旧版运行时的便捷方法(如 setRiveResource())不提供直接跟踪加载状态或处理错误的方式。相反,必须首先使用 File 类加载文件。加载通常是同步的,但可以在后台线程上执行。通过一些努力,可以实现类似的加载状态跟踪。

目前没有计划将此功能改造到旧版运行时中。

渲染到 Bitmap​

新运行时提供了内置支持,用于将 Rive 渲染到 Android Bitmap,这对于快照测试或将数据绑定的 Rive 文件渲染成图像帧以在边缘(即用户设备上)编码视频等场景非常有用。这可以通过 RenderBuffer 类或 Rive 可组合项中的 onBitmapAvailable 回调来实现。

在旧版运行时中,通过渲染到由 Bitmap 支持的 Canvas 或子类化渲染器,在技术上是可行的,但这不是内置功能,需要更多努力来实现。

目前没有计划将此功能改造到旧版运行时中。

更新数据绑定会取消稳定状态机​

在新运行时中,更新数据绑定属性会自动"取消稳定"状态机,使其根据新数据推进和绘制。相比之下,旧版运行时要求你在稳定状态机上更新数据绑定属性后调用 RiveAnimationView.play() 才能达到相同的效果。

我们将来可能会考虑将此行为改造到旧版运行时中,因为这是常见的困惑来源。然而,这样做将是一个破坏性变更,对于现有用户来说可能不值得因此而中断。

旧版独占功能​

一些功能仅在旧版运行时中可用。这通常是因为它们不太常用、在实践中复杂或难以维护和支持。以下是这些功能的列表及如何在新运行时中处理其缺失的指导。

基于 View 的 API​

旧版运行时围绕 RiveAnimationView 类构建,该类是 Android TextureView(进而 View)的子类。这使得集成到现有的基于 View 的 Android 应用程序中变得容易。新运行时目前基于 Compose,不提供 View 子类。如果你需要在基于 View 的应用程序中使用 Rive,你需要使用 ComposeView 在你的 View 层次结构中设置 Compose 环境。

有计划在未来提供基于 View 的 API。

帧指标​

旧版运行时支持帧指标,提供一些渲染性能数据,可能有助于识别瓶颈。这通过启用 RiveAnimationView 上的帧指标收集来完成。

riveView.startFrameMetrics()

新运行时尚未包含任何类似的功能。相反,你可以使用 Android 内置的性能分析工具,如 Android Studio 中的 Android Profiler,来测量渲染性能并识别瓶颈。

我们将来会添加性能监控。具体形式待定。

CDN 资源​

旧版

Rive 编辑器允许将资源标记为"托管",意味着它被上传到 Rive 的 CDN。旧版运行时包含内置支持,在加载 Rive 文件时直接从 CDN 加载这些资源。使用 RiveAnimationView 类时,这是自动的且默认启用的。

旧版 RiveAnimationView

// Builder API(默认启用)
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setShouldLoadCDNAssets(true)
.build()

// 使用资源加载器(默认启用)
val riveView = RiveAnimationView(context)
riveView.setAssetLoader(FallbackAssetLoader(context, loadCDNAssets = true))

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveShouldLoadCDNAssets="true" />

新版

新运行时没有内置支持加载 CDN 资源。

我们将在未来的更新中添加新的 API 来查询文件的资源清单,其中将包含托管资源的 URL。这将允许你使用标准网络库加载它们,并使用现有的资源 API 将其提供给 Rive worker。

从网络设置 Rive 文件​

旧版运行时使用现已废弃的 Volley 库提供了从网络 URL 加载 Rive 文件的内置支持。包括以下方法:

旧版 Builder API

RiveAnimationView.Builder(context)
.setResource("https://example.com/animation.riv")

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveUrl="https://example.com/animation.riv" />

新运行时没有直接包含此功能。相反,你可以使用标准 Android 网络库(如 Retrofit、OkHttp)来获取文件,然后使用 RiveFileSource.Bytes 源加载,传入下载的字节。在 Compose 中,可以将此源提供给 rememberRiveFile;在 Compose 上下文之外使用 RiveFile.fromSource。

我们正在考虑在未来版本中添加内置的网络加载功能。如果实现,将不会使用 Volley,而是选择现代替代方案。它也可能默认是可选加入的,以避免为不需要网络加载的用户添加不必要的依赖。

渲染到 Canvas​

有关更多详细信息,另请参阅选择渲染器。

旧版运行时包含一个 Canvas 渲染器,允许将 Rive 动画直接渲染到 Android Canvas 中。这在 Rive 渲染器功能尚未兼容时,作为一个后备方案非常有用。随着我们对 Rive 渲染器的改进,此用例已减少。

Canvas 渲染器通过以下 API 启用。

Rive 全局默认值

Rive.defaultRendererType = RendererType.Canvas

旧版 RiveAnimationView Builder

RiveAnimationView.Builder(context)
.setRendererType(RendererType.Canvas)

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveRenderer="Canvas" />

新运行时没有包含 Canvas 渲染器,而是专注于 Rive 渲染器以获得最佳性能和功能支持(如矢量羽化)。此外,与 Rive 渲染器相比,Canvas 渲染器在性能和视觉保真度方面存在限制。

目前没有计划在新运行时中重新引入 Canvas 渲染。

画板音量​

旧版运行时可以通过 Artboard.volume 属性或 RiveAnimationView.setVolume() 设置画板的音频音量。新运行时中没有此功能。

我们将来可能会考虑添加音频功能,但目前没有即时计划。

状态机输入​

有关更多详细信息,另请参阅输入文档。

旧版运行时提供了设置状态机输入的方法。

riveView.setNumberState("My State Machine", "My Number Input", 42.0f)
riveView.setBooleanState("My State Machine", "My Boolean Input", true)
riveView.fireState("My State Machine", "My Trigger Input")

新运行时没有包含这些方法。状态机输入已被弃用,取而代之的是数据绑定,它提供了一种更灵活、更强大的方式来管理状态机的更改。

Rive 编辑器在菜单 > "Convert Inputs to ViewModels"中提供了自动转换工具。这是一个 1:1 的转换,保留了现有行为,同时启用了数据绑定的好处,尽管这在性能或语义上可能不是最理想的。你可以在采用新运行时期间使用此功能执行初始迁移,然后迭代数据契约以进一步改进。

事件​

有关更多详细信息,另请参阅 Rive 事件文档。

旧版运行时支持监听 Rive 事件,以响应从 Rive 文件发出的事件。这通过 RiveFileController.Listener 接口完成。

riveView.addEventListener(object : RiveFileController.RiveEventListener {
override fun notifyEvent(event: RiveEvent) {
// 处理事件
}
})

新运行时没有包含此功能。事件已被弃用,取而代之的是数据绑定,它提供了一种更灵活、更强大的方式来监听 Rive 文件的更改。简单的事件可以用视图模型中的触发器属性替换。

目前没有计划在新运行时中重新引入事件监听。

文本运行​

有关更多详细信息,另请参阅文本文档。

旧版运行时支持文本运行,用于获取和设置 Rive 内容中的动态文本。

val text = riveView.getTextRunValue("My Text Run")
riveView.setTextRunValue("My Text Run", "Hello, World!")

新运行时没有包含此功能。文本运行已被弃用,取而代之的是数据绑定,它通过字符串属性提供了一种更灵活、更强大的方式来管理动态文本内容。任何以前按名称寻址的文本运行必须绑定到视图模型中的字符串属性。

val vmi = rememberViewModelInstance(riveFile)

// 从 flow 收集
val text by vmi.getStringFlow("My Text Property").collectAsState(initial = "")

// 或命令式获取
LaunchedEffect(vmi) {
val text = vmi.getStringFlow("My Text Property").first()
}

vmi.setString("My Text Property", "Hello, World!")

目前没有计划在新运行时中重新引入文本运行支持。

线性动画​

有关更多详细信息,另请参阅线性动画文档。

在 Rive 编辑器的动画模式中,设计师可以创建状态机和线性动画。旧版 Android 运行时支持直接播放多个线性动画,允许开发者在不使用状态机的情况下触发这些动画。这些包括循环模式、方向、持续时间和搜索。

旧版 Kotlin 方法

// 查询线性动画名称列表
val linearAnimations = riveView.animations

// 查询正在播放的动画
val playingAnimations = riveView.playingAnimations

// 从 Builder API 播放线性动画
RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAnimationName("My Animation")

// 使用 set 方法播放线性动画
riveView.setRiveResource(R.raw.my_rive_file, animationName = "My Animation")

// 直接播放线性动画
riveView.play("My Animation")

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveAnimation="My Animation" />

在实践中,使用线性动画比状态机更细粒度且更脆弱,因此它们在新运行时中不直接支持。要实现类似的功能,你可以在 Rive 编辑器中创建一个状态机来播放所需的线性动画,然后从你的 Android 代码中触发该状态机。

目前没有计划在新运行时中重新引入直接线性动画播放。

每个视图多个状态机​

旧版运行时允许多个状态机与单个 RiveAnimationView 实例关联。在核心运行时层面没有技术限制,每个状态机按顺序处理。在实践中,这会导致复杂性和状态推理困难,尤其是在涉及数据绑定时。在各种 Rive 运行时中,只有 Android 旧版运行时支持此功能。

新运行时强制 Rive 实例与状态机之间的一对一关系,以简化状态管理。如果你需要管理并发的、重叠的状态,请考虑使用状态机图层。

目前没有计划在新运行时中重新引入每个 Rive 实例多个状态机。

观察状态机状态​

旧版运行时提供了观察状态机中当前处于活动状态的方法。这对于根据动画的当前状态在应用程序中触发操作非常有用。

riveView.registerListener(object : RiveFileController.Listener {
override fun notifyStateChanged(stateMachineName: String, stateName: String) {
// 处理状态更改
}
})

新运行时没有包含此功能。原始功能是在数据绑定之前添加的,对 Rive 文件的更改很脆弱。现代等效做法是通过数据绑定属性在状态更改时发出值。这意味着与 Rive 文件的耦合更松散,允许设计师迭代而不会破坏应用程序代码。

目前没有计划在新运行时中重新引入状态观察。

单点触控支持​

旧版运行时支持单点触控输入,一次只允许一个触控交互。这是为了保持与设计时考虑单点触控的现有 Rive 文件的向后兼容性。因此,这也是默认行为。

旧版 RiveAnimationView

// Builder API
val riveView = RiveAnimationView.Builder(context)
.setMultiTouchEnabled(false)
.build()

// 属性
riveView.multiTouchEnabled = false

旧版 XML 布局

<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveMultiTouchEnabled="false" />

新运行时仅支持多点触控输入。在实践中,Rive 文件很少需要区分单点触控和多点触控,因为触控点通常用于触发交互而不是单独跟踪。在迁移时测试你的 Rive 文件,确保它们在多点触控输入下表现如预期,并在必要时调整设计。

目前没有计划在新运行时中重新引入单点触控支持。

按索引获取​

旧版运行时提供了按索引(除了按名称)获取画板、动画、状态机、视图模型和视图模型实例的方法。这在你想迭代所有元素的场景中很少有用。

val artboard = riveFile.artboard(0)
val animation = artboard.animation(0)
val stateMachine = artboard.stateMachine(0)
val viewModel = riveFile.getViewModelByIndex(0)
val instance = viewModel.createInstanceFromIndex(0)

新运行时可以通过迭代查询到的名称来实现类似功能。以画板为例:

LaunchedEffect(riveFile) {
val artboardNames = riveFile.getArtboardNames()
for (name in artboardNames) {
Artboard.fromFile(riveFile, name).use {
// 使用画板
}
}
}

目前没有计划在新运行时中重新引入按索引获取。

渲染器类​

旧版运行时包含 Renderer 和 RiveArtboardRenderer 类,允许你覆盖 Rive 的渲染行为。它很难正确使用,确保正确的生命周期,并随着 Rive 渲染器的发展保持兼容性。

新运行时没有包含 Renderer 类,而是专注于最常见的用例。其他用例(如渲染到 Android Bitmap)通过专用 API 支持。渲染循环由 Rive 可组合项管理,并在 RiveWorker 上执行。

目前没有计划在新运行时中重新引入渲染器类。

控制器类​

旧版运行时包含一个 RiveFileController 类,提供控制播放、管理状态机和观察状态更改的方法。它与 RiveAnimationView 紧密耦合。

新运行时不需要单独的控制器类,因为控制直接通过 Rive 可组合项和相关辅助函数提供。

目前没有计划在新运行时中重新引入控制器类。

看完还有疑问?进群交流下!
与众多 Rive 创作者、开发者一起交流探讨与答疑解惑。
加入交流群