聲明式 UI?Android 官方怒推的 Jetpack Compose 究竟是什麼

視頻先行

若是你方便看視頻,直接去 嗶哩嗶哩 或者 YouTube 看視頻就好,下面的文章就不用看了。若是你不方便看視頻,下面是視頻內容的腳本整理稿。android

開始

聲明式 UI;更簡單的自定義;實時的、帶交互的預覽功能;還有更強的性能和功能。這就是 Android 官方全新推出的 UI 框架——Jetpack Compose。web

你們好,我是扔物線朱凱。算法

2019 年中,Google 在 I/O 大會上公佈了 Android 最新的 UI 框架:Jetpack Compose。Compose 能夠說是 Android 官方有史以來動做最大的一個庫了。它在 2019 年中就公佈了,但要到今年也就是 2021 年纔會正式發佈。這兩年的時間 Android 團隊在幹嗎?在開發這個庫,在開發 Compose。一個 UI 框架而已,爲何要花兩年來打造呢?由於 Compose 並非像 RecyclerViewConstraintLayout 這種作了一個或者幾個高級的 UI 控件,而是直接拋棄了咱們寫了 N 年的 ViewViewGroup 那一套東西,從上到下擼了一整套全新的 UI 框架。直白點說就是,它的渲染機制、佈局機制、觸摸算法以及 UI 的具體寫法,全都是新的。數組

Compose 的寫法

Compose 從一出現,最受到官方推崇以及關注者讚賞的就是它實現了聲明式 UI,說它比咱們傳統寫法的「命令式 UI」怎麼怎麼好——咱們傳統的 ViewViewGroup 那一套系統的寫法叫「命令式」。可是對於大多數 Android 開發者來講,咱們的第一個問題就是:什麼是「聲明式 UI」?markdown

在講「聲明式 UI」以前,咱們先看一下 Compose 的代碼長什麼樣。Compose 是用 Kotlin 來寫的,它的每一個控件都是一個函數調用。好比你要顯示一塊文字,你就這麼寫:框架

Text("Hello")
複製代碼

看起來好像只是調用構造函數建立了一個對象,但這麼寫就已經能夠顯示出一塊文字來了。ide

另外——這其實也並無建立對象,這個 Text() 也不是一個構造函數,而是一個普通函數。svg

Text("Hello")

...

@Composable
fun Text(...) {
    ...
}
複製代碼

普通函數用大寫開頭幹嗎?很簡單,爲了辨識度。Compose 規定了這種大寫開頭的命名方式,這樣咱們就能一眼認出來:哦,這是個 Compose 的函數——或者用更官方的叫法:這是一個 Composable。函數

到這兒有人可能就會想:這個 Text() 它實質上是個什麼?是個 TextView 嗎?不是的。剛纔我說過一次,Compose 的渲染機制、佈局機制、觸摸機制全都是新寫的,因此這個 Text() 的底層不是 TextView,也不是任何一個原生控件,而是直接調用了更下層的繪製 API,也就是 Canvas 那一套東西。同理,Compose 裏的各個組件,都是獨立的新實現。oop

好繼續說。一個函數調用是一個組件;兩個函數調用就是兩個組件;

Text("Hello")
Image()
複製代碼

多個函數組合起來,就是一個完整的界面:

Column {
    Text("Hello")
    Image()
}
複製代碼

這,就是 Compose 的寫法。看完它的寫法,咱們就能夠回到剛纔的問題:什麼是「聲明式 UI」?這段代碼怎麼就「聲明式」了?它和咱們一直以來的寫法有什麼區別?

首先,咱們通常怎麼寫 UI 的?xml 文件,對吧?好比這個界面,上下排列的一塊文字和一個圖片,它的等價傳統寫法是這樣的:

<!-- 代碼通過必定簡化 -->
<LinearLayout>
  <TextView android:text="Hello" />
  <ImageView />
</LinearLayout>
複製代碼

一個 LinearLayout,裏面包着一個 TextView 和一個 ImageView

看了之後什麼感受?大同小異是吧?除了名字換換、格式變變,大致上是同樣的。對吧?

那爲何左邊就叫命令式,右邊就叫聲明式呢?xml 命令誰了?以及,右邊這寫法怎麼就更優秀了?我爲何要學一個看起來並無什麼本質區別的新寫法來爲難本身?

其實所謂「聲明式 UI」,指的是你只須要把界面給「聲明」出來,而不須要手動更新。關鍵在於「不須要手動更新」。好比左邊這個佈局裏的 TextView,若是它對應的數據改變了,我要怎麼把新的文字更新到它?很簡單:findViewById()setText() 對吧?

findViewById()
setText()
複製代碼

而若是用 Compose 呢?怎麼更新?不用更新。由於 Compose 的界面會隨着數據自動更新。

Compose 會對界面中用到的數據自動進行訂閱——不論是字符串仍是圖像仍是別的什麼,Compose 所有可以自動訂閱——這樣當數據改變的時候,Compose 會直接把新的數據更新到界面。

var text = "Hello"

...

Column {
    Text(text)
    Image()
}
複製代碼

這個「自動訂閱」的功能很容易使用,你只要在初始化的時候加上一個 by mutableStateOf() ,剩下的全都由 Compose 自動搞定。

var text by mutableStateOf("Hello")

...

Column {
    Text(text)
    Image()
}
複製代碼

這個神奇的功能是利用 Kotlin 的 Property Delegation 屬性委託來實現的。這也在必定程度上回答了一個問題: 爲何 Compose 只能用 Kotlin 寫,而不能用 Java?由於它用了大量的 Kotlin 特性,而這些特性用 Java 不能簡單實現。注意,雖然 Kotlin 和 Java 是兼容的,Kotlin 能作到的事 Java 也能作到,可是有些東西它「不能簡單實現」就約等於不能實現了,由於不實用啊!對吧?因此 Android 自稱永遠不放棄對 Java 的支持,他們就這麼一說,你就這麼一聽,不要真的就不學 Kotlin,否則會愈來愈難受。你看這 Compose 不是已經在逼着咱們用 Kotlin 了嗎?

好拐回來,這就是所謂的「聲明式 UI」:你只要聲明界面是什麼樣子,不用手動去更新,由於界面會自動更新。而傳統的寫法裏,數據發生了改變,咱們得手動用 Java 代碼或者 Kotlin 代碼去把新數據更新到界面。你給出詳細的步驟,去命令界面進行更新,這就是所謂的「命令式 UI」。

那麼如今咱們再往回拐:傳統的 xml 寫法和 Compose 的 Kotlin 寫法,爲何一個是「命令式」,一個是「聲明式」?這個問題其實自己就是錯的。單單一段 xml 代碼並不能稱做是命令式 UI。傳統寫法的「命令式」並不在於 xml 部分,而在於 Java 部分:Java 代碼去指揮、去命令界面更新,這纔是「命令式」的含義所在;而 Compose 經過訂閱機制來自動更新,因此不須要作這種「命令」,因此是「聲明式」。

因此你看,不論是聲明式仍是命令式,跟 xml 和 Kotlin 是無關的,它們並非語言角度的定義,也不是寫法角度的定義,而是——功能角度。一個 UI 框架,若是可讓開發者只聲明出界面的樣子,而不用去寫各類界面更新的代碼,它就是一個聲明式的 UI 框架。換句話說,若是 Android 可讓咱們用 xml 寫的界面也和數據作關聯,讓界面自動更新而不須要開發者手寫更新代碼,那麼它就也是聲明式 UI。聲明式 UI 是一種強大的功能,而不是一種優秀的代碼風格。

哎?數據和界面作關聯,界面跟着數據自動更新,這不就是數據綁定嗎?Android 已經有這樣的官方庫了啊!就叫 Data Binding,是吧?我用它不就得了,爲何要費這麼大勁去用 Compose 呢?

首先,對!Data Binding 和 Compose 本質上都是經過界面對數據進行訂閱來實現了界面的自動更新,但!它們是有關鍵區別的。區別就在於,Data Binding 經過數據更新的只能是界面元素的值,而 Compose 能夠更新界面中的任何內容,包括界面的結構。好比你用一個 Boolean 類型的變量控制界面中某個元素是否顯示,

var text = ...
var showImage = ...
Column {
    Text(text)
    if (showImage) {
        Image()
    }
}
複製代碼

當你把變量的值從 true 變成 false 的時候,

var text = ...
var showImage = ...
Column {
    Text(text)
    if (showImage) {
        Image()
    }
}

...

showImage = false
複製代碼

這個元素會從界面中徹底消失,就像歷來沒有出現過同樣,而不是用 setVisibility(GONE) 這種方式從視覺上隱藏。這兩種策略看起來好像區別不大,那是由於我舉的例子簡單,實際上這是一種機制的改變,而這種機制的改變給界面開發帶來的靈活性和性能的提高是很是大的。你想一下,是否是?

總結

因此「聲明式 UI」還真的不是個噱頭,它讓 Compose 比傳統的 UI 系統強大得多。並且如今除了 Android 的 Compose 以外,iOS 的 SwiftUI 以及跨平臺的 Flutter 也都是聲明式的。聲明式 UI 已是一種趨勢了。

固然了無論怎麼說,客觀地講,Compose 確實是一套比較難學的東西,由於它畢竟太新也太大了,它是一個完整的、全新的框架,確實讓不少人感受「學不動」,這也是個事實。那怎麼辦呢?學不動怎麼辦呢?

個人建議是——

學。

相關文章
相關標籤/搜索