何为Compose
2019 年中,Google 在 I/O 大会上公布的用于Android构建原生界面的全新 UI 框架。也就是说它的渲染机制、布局机制、触摸算法以及 UI 的具体写法,全都是新的。啊,这难道要把我们Android开发者赖以生存的技能---画xml,给点灭啦?
为啥要学
作为Google新推出的一套全新ui框架,Compose有别于传统的xml+java(kotlin)的“命令式ui”写法,它是一种“声明式ui”,iOS 的 SwiftUI 以及跨平台的 Flutter 也都是声明式的,可见声明式 UI 已经是一种趋势了(扶我起来,我还能学.jpg)。那它有那些好处呢?
- 更少的代码
用更少的代码来完成更多的功能,简单,易于维护,补充一下,Compose只能用Kotlin语言编写噢! - 直观
只需要把ui元素描述出来即可,其他的交给Compose处理即可,一旦状态发生变化,你的ui也会自动更新 - 加速开发
兼容现有代码,Android Studio的实时预览便于快速迭代 - 功能强大
Android 平台 API 的直接访问和对于 Material Design、深色主题、动画等的内置支持
Compose vs Xml+Java/Kotlin
前者只要声明界面是什么样子,不用手动去更新,因为界面会自动更新,而且去掉了xml,只需使用Kotlin即可。而后者数据发生了改变,我们得手动用 Java 代码或者 Kotlin 代码去把新数据更新到界面。给出详细的步骤,去命令界面进行更新。
xml
setContentView(R.layout.activity_compare)
findViewById(R.id.tv_greeting).text = "Hello Android"
compose
@Preview
@Composable
fun FirstPreview() {
var name by remember { mutableStateOf("Hello Compose") }
Story(name)
name = "Hello Android"
}
@Composable
fun Story(name: String) {
Text(name, textAlign = TextAlign.Center)
}
状态管理
状态
app中的状态(State)指的是一个可以随着时间变化的值。我们的应用就是在向用户展示状态。Compose可以让我们明确状态存储的位置和方式。
组合和重组
组合(composition)就是由一个个Composable调用形成的树形结构,运行这些Composable便可展示出我们的ui,在初识组合期间,Compose会对这些构建ui的Composable函数进行跟踪,当app的状态发生改变时,Compose会进行一次重组(recomposition)来更新ui。
组合只能由初始组合产生,并且只能由重组更新。修改的唯一方法就是重组。
Talk is cheap, show me the code
// this stateful composable is only responsible for holding internal state
// and defers the UI to the stateless composable
@Composable
fun ExpandingCard(title: String, body: String) {
var expanded by remember { mutableStateOf(false) }
ExpandingCard(
title = title,
body = body,
expanded = expanded,
onExpand = { expanded = true },
onCollapse = { expanded = false }
)
}
// this stateless composable is responsible for describing the UI based on the state
// passed to it and firing events in response to the buttons being pressed
@Composable
fun ExpandingCard(
title: String,
body: String,
expanded: Boolean,
onExpand: () -> Unit,
onCollapse: () -> Unit
) {
Card {
Column(
Modifier
.width(280.dp)
.animateContentSize() // automatically animate size when it changes
.padding(top = 16.dp, start = 16.dp, end = 16.dp)
) {
Text(title)
if (expanded) {
Spacer(Modifier.height(8.dp))
Text(body)
IconButton(onClick = onCollapse, Modifier.fillMaxWidth()) {
Icon(Icons.Default.KeyboardArrowUp, "Expand Less")
}
} else {
IconButton(onClick = onExpand, Modifier.fillMaxWidth()) {
Icon(Icons.Default.KeyboardArrowDown, contentDescription = "Expand more")
}
}
}
}
}
其中mutableState会返回一个MutableState,是一个可被观察的类型,它的值一旦发生变化,那么任何使用它的值的Composable函数将会发生重组,然后更新组合。remember的作用就是将状态的值存储在内存中,给Composable函数赋予”记忆“的功能,不会在每次重组中将上一次的值丢失。然而当发生切换屏幕方向,切换语言这些configuration changes时,remember便无能为力了,这时候rememberSaveable就可以派上用场啦。rememberSaveable可以自动将任何可以放在Bundle的值存储下来。
Composable的生命周期
互操作性
Xml里使用Compose
通过使用ComposeView然后调用setContent方法即可
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_compose_in_xml)
findViewById(R.id.compose_view).setContent {
Text("Hello Compse")
}
}
Compose里使用Android View
通过使用AndoridView可以引入一个view
@Composable
private fun PlantDescription(description: String) {
// Remembers the HTML formatted description. Re-executes on a new description
val htmlDescription = remember(description) {
HtmlCompat.fromHtml(description, HtmlCompat.FROM_HTML_MODE_COMPACT)
}
// Displays the TextView on the screen and updates with the HTML description when inflated
// Updates to htmlDescription will make AndroidView recompose and update the text
AndroidView(
factory = { context ->
TextView(context).apply {
movementMethod = LinkMovementMethod.getInstance()
}
},
update = {
it.text = htmlDescription
}
)
}
@Preview
@Composable
private fun PlantDescriptionPreview() {
MaterialTheme {
PlantDescription("HTML
description")
}
}
学习资源
https://developer.android.com/jetpack/compose/state?hl=zh-cn