何為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<TextView>(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方法即可
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".compare.ComposeInXmlActivity">
<TextView
android:id="@+id/hello_world"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Hello Android!" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
</LinearLayout>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_compose_in_xml)
findViewById<ComposeView>(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<br><br>description")
}
}
學習資源
https://developer.android.com/jetpack/compose/state?hl=zh-cn