本系列是我学习compose过程中,对官方文档的翻译和解读,以及实验性的Demo工程。主要参考官方文档和中文手册
全部的正文内容(Demo工程除外)源自Compose官方文档,个人解读以引用的形式插入。
Compose 官方文档 https://developer.android.google.cn/jetpack/compose
Compose 中文手册 https://compose.net.cn/
本文翻译内容 https://developer.android.google.cn/jetpack/compose/mental-model
Jetpack Compose
是Android一个现代化的声明式UI
工具包。Compose使编写和维护UI
代码变得很容易,它提供了声明式的Api
允许我们在不用绘制 view 的前提下渲染出想要的UI
声明式的编程模型
历史上,Android的UI
结构是用view树的形式来组织的。当应用的状态随着用户的操作而变化时,这个树结构需要根据当前数据进行更新。最常见的更新方法是使用findViewById()
遍历view树找到对应view节点,然后调用 button.setText(String)
, container.addChild(View)
, 或者img.setImageBitmap(Bitmap)
这类函数更新节点。这些方法会改变这些组件的内部状态
手动操作view会增加犯错的可能。如果一个数据需要在多个view上展示,那在数据更新后很容易忘记更新其中某个view。另外当两个更新操作冲突时,会导致一些错误的更新结果。例如,某个更新操作需要对某个节点设置一个值,而这个节点刚刚被移出了view树。通常来说,软件维护的成本随着view数目的增加而增加
在过去的几年里,整个行业都在转向声明式的UI
框架,它能大大简化UI
相关的工程量。该技术的工作原理是从头开始重新生成整个屏幕,然后只应用必要的更改。这种方法避免了手动更新有状态的视图带来的复杂性。Compose是一个声明式的UI
框架。
重新生成整个屏幕的一大难点在于昂贵的开销,包括运行时间、计算性能和电量消耗。为了降低成本,Compose在任意给定的时间会智能地选择需要重绘的UI
部分。这种原理对如何设计你的UI
结构会有影响,下文会继续讨论。
这一小节其实解释了声明式
UI
兴起的根本原因,原有的view tree不适合越来越复杂的应用
一个简单的可组合函数
你可以用一系列可组合函数(composable function
)来构建你的UI
视图,这些函数接收一些数据的输入,然后生成UI
组件。一个简单的例子是Greeting
组件,接收一个String
类型输入,然后生成一个Text
组件并展示一个欢迎的信息
关于这个函数的一些注意事项:
- 所有的可组合函数都要有
@Composable
注解。这个注解会通知Compose
编译器这个函数需要把数据转换成UI
- 可组合函数接收一些描述
UI
逻辑的参数。这个例子中接受一个String
用来生成欢迎语句 - 可组合函数通过调用其他可组合函数来生成
UI
组件。这里调用了Text()
生成一个文本 - 这个函数没有返回值。可组合函数生成
UI
都不需要返回值,因为它只是在描述需要一个什么样的UI
而不是构建一个UI
组件 - 可组合函数是快速的、幂等的、没有副作用的
- 使用相同的参数调用这个函数,无论调多少次,它的结果都是完全相同的,因为它的执行并不依赖全局变量
- 这个函数没有副作用是指它不会对外部的变量造成任何影响T
理论上我们编写的所有可组合函数都要满足这些特点
这一小节讲了compose开发的核心工具:可组合函数。没有提原理,但是讲了几点约束。其中所谓幂等的和没有副作用的,其实就是设计模式中老生常谈的高内聚,低耦合,函数体里的全部逻辑应该只依赖于入参
声明式模型带来的变化
使用面向对象的UI
工具包,你需要实例化一个view树来初始化整个UI
结构。我们通常使用xml
文件来做实现这点。每一个组件持有它内部的状态,通过暴露getter 和 setter方法允许应用控制它的逻辑
而在compose的声明式模型里,组件是没有状态的,也不暴露getter和setter方法。事实上组件并不被视为一个对象。你通过对相同的可组合函数传入不同的参数来控制一个UI
的展示。这非常契合ViewModel
提供的observable
。可组合函数只要负责监听observable
,在其变化时更新UI
即可
当用户与UI
交互时,UI
需要上报这些事件比如onClick
。这些事件需要通知应用的逻辑层,逻辑层会依据这些改变状态。当状态改变时,监听这些状态的可组合函数被调用,然后使用新的数据重绘UI
。这个过程叫作recomposition
这节讲的是compose的架构逻辑,其实思想很早就有了,就是
MVVM
,但是compose比传统view更契合这个思想。因为它的UI
是不包含状态,和状态是完全解耦的。这样开发者只需要在ViewModel
里注册好各种Observable
,然后在Activity或者Fragment里监听它们,监听回调里调用对应的可组合函数。类似的,当点击等交互事件发生时,更改ViewModel
里对应的Observable
,然后就再次发生回调刷新UI
动态的内容
因为可组合函数是用Kotlin
而非xml<