何為Compose
2019 年中乏德,Google 在 I/O 大會(huì)上公布的用于Android構(gòu)建原生界面的全新 UI 框架洪唐。也就是說(shuō)它的渲染機(jī)制谐鼎、布局機(jī)制唯咬、觸摸算法以及 UI 的具體寫(xiě)法席楚,全都是新的咬崔。啊,這難道要把我們Android開(kāi)發(fā)者賴以生存的技能---畫(huà)xml烦秩,給點(diǎn)滅啦垮斯?
為啥要學(xué)
作為Google新推出的一套全新ui框架,Compose有別于傳統(tǒng)的xml+java(kotlin)的“命令式ui”寫(xiě)法只祠,它是一種“聲明式ui”甚脉,iOS 的 SwiftUI 以及跨平臺(tái)的 Flutter 也都是聲明式的,可見(jiàn)聲明式 UI 已經(jīng)是一種趨勢(shì)了(扶我起來(lái)铆农,我還能學(xué).jpg)牺氨。那它有那些好處呢?
- 更少的代碼
用更少的代碼來(lái)完成更多的功能墩剖,簡(jiǎn)單猴凹,易于維護(hù),補(bǔ)充一下岭皂,Compose只能用Kotlin語(yǔ)言編寫(xiě)噢郊霎! - 直觀
只需要把ui元素描述出來(lái)即可,其他的交給Compose處理即可爷绘,一旦狀態(tài)發(fā)生變化书劝,你的ui也會(huì)自動(dòng)更新 - 加速開(kāi)發(fā)
兼容現(xiàn)有代碼,Android Studio的實(shí)時(shí)預(yù)覽便于快速迭代 - 功能強(qiáng)大
Android 平臺(tái) API 的直接訪問(wèn)和對(duì)于 Material Design土至、深色主題购对、動(dòng)畫(huà)等的內(nèi)置支持
Compose vs Xml+Java/Kotlin
前者只要聲明界面是什么樣子,不用手動(dòng)去更新陶因,因?yàn)榻缑鏁?huì)自動(dòng)更新骡苞,而且去掉了xml,只需使用Kotlin即可。而后者數(shù)據(jù)發(fā)生了改變解幽,我們得手動(dòng)用 Java 代碼或者 Kotlin 代碼去把新數(shù)據(jù)更新到界面贴见。給出詳細(xì)的步驟,去命令界面進(jìn)行更新躲株。
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)
}
狀態(tài)管理
狀態(tài)
app中的狀態(tài)(State)指的是一個(gè)可以隨著時(shí)間變化的值片部。我們的應(yīng)用就是在向用戶展示狀態(tài)。Compose可以讓我們明確狀態(tài)存儲(chǔ)的位置和方式霜定。
組合和重組
組合(composition)就是由一個(gè)個(gè)Composable調(diào)用形成的樹(shù)形結(jié)構(gòu)吞琐,運(yùn)行這些Composable便可展示出我們的ui,在初識(shí)組合期間,Compose會(huì)對(duì)這些構(gòu)建ui的Composable函數(shù)進(jìn)行跟蹤然爆,當(dāng)app的狀態(tài)發(fā)生改變時(shí),Compose會(huì)進(jìn)行一次重組(recomposition)來(lái)更新ui黍图。
組合只能由初始組合產(chǎn)生曾雕,并且只能由重組更新。修改的唯一方法就是重組助被。
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會(huì)返回一個(gè)MutableState,是一個(gè)可被觀察的類型剖张,它的值一旦發(fā)生變化,那么任何使用它的值的Composable函數(shù)將會(huì)發(fā)生重組揩环,然后更新組合搔弄。remember的作用就是將狀態(tài)的值存儲(chǔ)在內(nèi)存中,給Composable函數(shù)賦予”記憶“的功能丰滑,不會(huì)在每次重組中將上一次的值丟失顾犹。然而當(dāng)發(fā)生切換屏幕方向,切換語(yǔ)言這些configuration changes時(shí)褒墨,remember便無(wú)能為力了炫刷,這時(shí)候rememberSaveable就可以派上用場(chǎng)啦。rememberSaveable可以自動(dòng)將任何可以放在Bundle的值存儲(chǔ)下來(lái)郁妈。
Composable的生命周期
互操作性
Xml里使用Compose
通過(guò)使用ComposeView然后調(diào)用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
通過(guò)使用AndoridView可以引入一個(gè)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")
}
}
學(xué)習(xí)資源
https://developer.android.com/jetpack/compose/state?hl=zh-cn