本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Redwood
Redwood是一个开源库, 使用Kotlin构建响应式Android, iOS, 和web UI.
Redwood当前处于开发之中并没有准备好对外使用.
响度式UI
Android和iOS UI框架提供用户交互为模型作为‘可变视图树’或者文档对象模型(DOM). 要使用可变对象树抽象构建应用, 编程人员执行2个离散步骤:
- 构建静态视图树. 在Android中, 构建静态视图树的传统工具是XML布局, 尽管我们已经使用Contour做了相同的很酷的工作, 使用Kotlin lambda来构建视图.
- 让视图动起来. 视图树应该随着用户交互(像点击按钮)和外部事件(像数据加载)进行变化. 程序变更视图树以表示当前应用状态. 一些变更立即改变了屏幕上的UI; 其它的则从旧状态到新状态顺滑地执行动画.
React使新的编程模型流行起来, 即响应式UI. 有了响应式UI, 编程人员写了render()函数, 该函数接收了应用状态并返回了视图树. 框架用初始应用状态调用了这个函数, 并且之后每一次应用状态变更都会再次调用. 该框架分析视图树对之间的差异并更新显示, 包括在适当的地方设置过渡动画.
在React中, render函数返回的视图树叫做虚拟DOM, 并且在它有一个屏幕上的等价物叫真实DOM. 虚拟DOM是一个简单JS值对象树; 真实DOM是一个实时浏览器HTML组件树. 创建并遍历数以千计的虚拟DOM对象是很快的; 创建数以千计的HTML组件却并不然! 所以, 虚拟DOM优化便是React做的魔法.
Compose
Jetpack Compose是Android平台响应式UI模型的一个实现. 它使用了实现技术进一步优化了响应式编程模型. 这通过2个互补模块来实现:
- Compose编译器 是一个Kotlin编译器插件, 支持函数的部分重估. 编程人员依然写渲染函数将应用状态转化为视图树. 编译器重写了这个函数追踪哪个输入产生哪个输出. 当应用状态输入变更的时候, 它只评估对于生成对应视图树变更必要的东西.
- Compose UI 是新的Android UI组件集合, 设计出来与Compose编译器一起工作. 它致函了Android视图系统长期以来的技术债.
Compose编译器重写的Kotlin函数叫做可重组函数. 可重组函数的部分重估叫做重组.
注意: Compose编译器没有Compose UI也可以使用. 比如, compose服务器侧在服务器上渲染了HTML组件, 这些组件通过WebSocket发送到了浏览器上.
设计系统
在Cash应用上我们使用了一个设计系统. 它详细指定了UI并且命名了它的元素:
- 标准颜色, 字体, 图标和维度的名字
- 使用上述名字命名了文本块
- 命名了控制, 比如标准确认框, 按钮, 和对话框
设计系统有助于编程人员和设计人员之间的协作. 它也增加了应用内和跨平台的一致性.
什么是Redwood?
Redwood集成了Compose编译器, 设计系统, 和平台相关的显示集合. 每一个Redwood项目由3部分实现:
- 设计系统. Redwood包含了一个叫做‘Sunspot’的设计系统例子. 大多数应用应该自定义这个设计系统以匹配他们的产品需求.
- UI平台显示. 显示在屏幕上绘制设计系统的像素. 显示能够为任意UI平台实现. Redwood对Android, iOS和web平台引入了Sunspot显示例子.
- 可组合函数. 这是客户端的逻辑, 接收应用状态并返回设计系统的元素. 这些跟MVP系统中的Presenter有相似的职责.
为什么是Redwood?
我们渴望开始写响应式UI! 但是我们不愿意继续跨iOS, Android和web平台重复代码. 尤其是, 我们不喜欢支持多个平台会降低我们的整体灵活性.
我们要想走缓慢的原生UI开发进程的捷径. Android UI上面的迭代要求缓慢的编译步骤和adb install步骤. 有了Redwood, 我们希望使用web作为我们开发目标, 尤其是我们在可组合函数变更上迭代时.
我们要想一个选项来修改应用的行为, 却不想等待用户更新他们的应用. 有了Kotlin/JS我们也许可以在应用开启时更新我们的可组合函数, 然后在JS虚拟机上运行. 我们甚至能够使用WebAssembly在没有性能损失的情况下达成这个目标.
为什么不是React Native?
React Native的确不可抗拒. 但是我们也已经读到集成RN到已有项目和小组的困难.
Redwood是一个包, 而非一个框架. 它设计用来增量适配并且低风险和与已有Android项目集成. 在iOS或者web应用中使用Redwood风险更大! 我们使用Kotlin移动跨平台有过好的体验, 并且期待Redwood有相似的输出.
代码例子
我们首先将设计系统表示为一组Kotlin数据类. Redwood将使用这些类为显示和可组合函数生成类型安全的API.
@Widget(1)
data class SunspotText(
@Property(1) val text: String?,
@Property(2) @Default(""black"") val color: String,
)
@Widget(2)
data class SunspotButton(
@Property(1) val text: String?,
@Property(2) @Default("true") val enabled: Boolean,
@Property(3) val onClick: () -> Unit,
)
显示通过使用原生UI组件实现了设计系统.
class AndroidSunspotText(
override val value: TextView,
) : SunspotText<View> {
override fun text(text: String?) {
value.text = text
}
override fun color(color: String) {
value.setTextColor(Color.parseColor(color))
}
}
可组合函数将应用状态渲染进设计系统. 这将使用Compose API的一些特性, 如remember()等.
@Composable
fun Counter(value: Int = 0) {
var count by remember { mutableStateOf(value) }
SunspotButton("-1", onClick = { count-- })
SunspotText(count.toString())
SunspotButton("+1", onClick = { count++ })
}