Jetpack Compose初識

何為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的生命周期

lifecycle-composition.png

互操作性

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容