Compose版本发展
19年,Compose在Google IO大会横空出世,大家都议论纷纷,为其前途堪忧。
21年7月Compose 1.0的正式发布,却让大家看到了Google在推广Compose上的坚决,这也注定Compose会成为UI开发的新风向。
23年1月 发布了1.4版本, 在不断更新迭代......
Compose是什么
Compose则是一个全新的UI库,隶属Jetpack中的一员,它的出现是为了重新定义Android UI的开发方式——声明式UI编程
Compose的优势
- 声明式UI,它基于声明式的UI编程模型,当数据发生改变时,UI将自动刷新。这意味着不再需要编写命令式代码来控制 UI 的每一个细节
- 去掉XML,完全解除了混合写法(xml+Java、kotlin)的局限性
- 超强兼容性,大多数常用库(如Navigation、ViewModel和Kotlin协程)都适用于Compose,Compose 能够与现有 View 体系并存,你可以为一个既有项目引入 Compose
- 加速开发,为我们提供了很多开箱即用的Material 组件,如果的APP是使用的material设计的话,那么使用Jetpack Compose 能让你节省不少精力。
- 精简代码数量,减少bug的出现
- 实时预览,Compose 预览机制可以做到与真机无异,真正的所见所即得
Compose和View的关系
Compose 是基于 Canvas渲染,它的原理是通过AndroidComposeView
的dispatchDraw
分发绘制,通过dispatchTouchEvent
分发手势,来实现「在同一个 View
的内部完成整个 UI 组件树」的效果。
AndroidComposeView
的作用是承上启下,作为Compose和View混合开发的桥梁,从而实现API互相调用的能力
Compose 是不会有能力上的天然限制的,也就是传统 View 方案能做的事 Compose 全都可以做,比如各种复杂的动画、手势、嵌套的多层级布局,Compose 都可以做到。
Compose 没有做出对等实现的只有
SurfaceView
和TextureView
这两个类,它们是用于高速刷新的内容的,比如视频播放或者相机的取景器界面。需要使用原生的SurfaceView
或者TextureView
。扩展: 直接在Android上使用skia引擎进行绘制UI,这样就和flutter完全一致了,不过google为了兼容原来的view没有选择skia这个方案,兼容性是有了,但是也限制了compose的性能。
声明式UI & 命令式UI
声明式UI和命令式UI是两种不同的编程风格。
在命令式UI中,需要手动构建一个全功能的UI实例,比如一个TextView文本,在随后UI发生变化时,调用set方法手动刷新UI。
fun timer(){
var count = 0
textView.setOnClickListener{
count+=1
textView.text = "count:${count}""
}
}
而在声明式UI中,开发人员描述当前的UI状态,数据更新后UI的刷新交给Compose框架。
@Composable
fun Timer(){
var count by remember { mutableStateOf(0) }
Text(text = "count:${count}", modifier = Modifier.clickable(onClick = count++))
}
Compose项目集成
使用Jetpack Compose 来开始你的开发工作有2种方式:
- 将Compose 依赖库添加到现有项目
- 创建一个支持Jetpack Compose的新应用
gradle 配置
在app目录下的build.gradle
中将app支持的最低API 版本设置为21或更高,同时开启Jetpack Compose enable
开关,代码如下:
kotlin编译器与Compose兼容性对应表
android {
buildFeatures {
compose = true
}
kotlinOptions {
jvmTarget = "1.8"
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.0"
}
}
Compose依赖添加
- 借助 Compose 物料清单 (BoM),只需指定 BoM 的版本,即可管理所有 Compose 依赖库版本
dependencies {
implementation platform('androidx.compose:compose-bom:2023.01.00')
implementation("androidx.compose.foundation:foundation")
implementation 'androidx.compose.ui:ui'
}
android推出的BOM(Bill of Material的缩写)来简化我们添加compose依赖过于繁杂的问题
-
为什么建议使用 BoM 管理 Compose 库版本?
- compose的一系列依赖,版本众多,更新且又频繁,且又相互有所依赖,对于我们开发来说,理清这些层层次次关系足以头大,然后还有个致命问题,我们几个库使用不同版本,可能还会导致编译直接报错,出现依赖版本等冲突问题
- 今后,Compose 库将单独进行版本控制,这意味着版本号将开始按照自己的节奏递增。每个库的最新稳定版本已经过测试,并保证能够很好地协同工作。不过,找到每个库的最新稳定版本可能比较困难,而 BoM 会帮助您自动使用这些最新版本
-
BoM 是否会自动将所有 Compose 库添加到我的应用中?
-
不会。需要再应用中实际添加和使用 Compose 库,必须在模块(应用级)Gradle 文件(通常是 app/build.gradle)中将每个库声明为单独的依赖项行。
-
使用 BoM 可确保应用中的任何 Compose 库版本兼容,但 BoM 实际上并不会将这些 Compose 库添加到您的应用中。
- 更新 BoM 版本时,使用的所有Compose库都会自动更新到新版本。
-
VersionCatlogs 依赖添加
BOM与Compose依赖版本对应表
// settings.gradle
....
versionCatlogs{
create('composeLibs'){
// Bom与Compose依赖库版本对应关系
// https://developer.android.com/jetpack/compose/bom/bom-mapping?hl=zh-cn
// 目前使用的是Bom:2023.01.00,但其中foundation使用了1.4.0-beta02,是因为该版本中LazyVerticalStaggeredGrid
// 才支持自定义跨列(spanCount)的能力
library('bom','androidx.compose','compose-bom').version('2023.01.00')
// material组件库,如下拉刷新
library('material', 'androidx.compose.material', 'material').withoutVersion()
// 基础ui组件库
library('ui', 'androidx.compose.ui', 'ui').withoutVersion()
// as预览
library('preview', 'androidx.compose.ui', 'ui-tooling-preview').withoutVersion()
library('tooling','androidx.compose.ui','ui-tooling').withoutVersion()
// 基础能力库 modifier修饰符,列表
library('foundation', 'androidx.compose.foundation', 'foundation').version('1.4.0-beta02')
// icon及扩展
library('icons', 'androidx.compose.material', 'material-icons-core').withoutVersion()
library('icons-ext', 'androidx.compose.material', 'material-icons-extended').withoutVersion()
library('activity-compose', 'androidx.activity', 'activity-compose').version('1.6.1')
// activity 和 viewmodel的扩展
library('viewmodel-compose', 'androidx.lifecycle', 'lifecycle-viewmodel-compose').version('2.5.1')
bundle('compose', ['material', 'ui', 'preview','tooling','icons', 'icons-ext','foundation','activity-compose','viewmodel-compose'])
}
}
添加一个 Text 元素
首先,我们会在 onCreate 方法中添加一个 Text 元素来显示一个 Hello World! 的文本。
setContent 块定义了一个我们可以调用 Composable
函数的 avtivity 的布局,Composable
函数只能从其他的 Composable
函数中调用
Jetpack Compose 使用一个 Kotlin 编译器插件来将这些 Composable
函数转化为应用程序的 UI
元素。例如,由 Compose UI
库定义的 Text()
函数就可以在屏幕上显示一个文本标签。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("Hello world!")
}
}
}
定义一个 composable 函数
-
Jetpack Compose
是围绕着Composable
函数建立的。要创建一个Composable
函数,只需在函数名称中添加@Composable
注解。 -
Composable
函数只能从其他Composable
函数的范围内调用。
为了更好的理解,定义一个 MessageCard()
函数,它包含了一个 name
参数,并使用这个参数来配置文本元素
class ComposeActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MessageCard("Hello world!!! Welcome to Compose")
}
}
// Composable 函数一般用大写开头,为了和普通的函数作为区分
@Composable
fun MessageCard(name: String) {
Text(text = name)
}
}
-
Android Studio
可以让你在IDE
中预览你的Composable
函数,而不需要部署到设备上。 -
但是有个限制, 需要预览的
Composable
函数必须不能有任何参数。因为这个限制,你不能直接预览MessageCard()
函数。 - 但是,你可以尝试写第一个叫
PreviewMessageCard()
的函数,它调用带有参数的MessageCard()
。在@Composable
之前添加@Preview
注解。
@Preview(showBackground = true)
@Composable
fun MessageCardPreview() {
TestApplicationTheme {
MessageCard("Hello world!!! Welcome to Compose")
}
}
消息卡片的搭建
到目前为止,我们已经建立了我们的第一个 Composable 的函数和预览! 为了发现更多的 Jetpack Compose 功能,我们将构建一个简单的页面结构,其中包含可以通过一些动画展开的消息列表。
data class Message(val author: String, val body: String)
// Composable 函数一般用大写开头,为了和普通的函数作为区分
@Composable
fun MessageCard(msg: Message) {
Row {
Text(text = msg.author)
Text(text = msg.body)
}
}
@Preview(showBackground = true)
@Composable
fun MessageCardPreview() {
TestApplicationTheme {
MessageCard(
msg = Message(
author = "Hello Compose",
body = "I am lovely-chubby"
)
)
}
}