發(fā)表時間:2019.11.7
在前不久的 Android Dev Summit '19 上哈踱,Jetpack Compose 終于發(fā)布了一個可直接獲得的預覽版∷铱睿現(xiàn)在的版本還是 0.1.0-dev02,處于非常早期的版本突想,官方也再三強調非常有可能產(chǎn)生變化且無法用于生產(chǎn)環(huán)境殴蹄。不過我認為這是簡單了解一下 Compose 的好時機究抓。有備而無患。
首先來了解一下現(xiàn)在嘗試 Compose 所需要的環(huán)境:
- Android Studio 4.0 Canary1
- Kotlin 1.3.60-eap-25
- minSdkVersion 21(也就是 Android 5.0)
好的袭灯,這篇文章就到此為止吧刺下。
等等!寫都寫了稽荧,我還是再多寫一點吧橘茉。
演這么一出其實也是為了說明,Compose 真的還處于很早期的階段姨丈,估計還需要很長一段路才能真正在項目中實際使用上畅卓。但早鳥多少能嘗到些甜頭。希望各位能通過簡單的了解蟋恬,預判一下以后的開發(fā)趨勢翁潘,趁早規(guī)劃,適時投資歼争。
同時由于 Compose 還處于很可能發(fā)生很多變化的狀態(tài)拜马,所以本文也不準備過多拘泥于使用的細節(jié),而是從更為大局的層面上介紹一下 Compose矾飞。當然一膨,筆者本人經(jīng)驗和眼界極為有限呀邢,真知灼見是沒有的洒沦,希望起到拋磚引玉的作用。
開始體驗
請記得下載一個最新的 Android Studio价淌,需要 4.0 版本申眼,在發(fā)文時這意味著你需要使用 canary channel 版本的。
開始新建一個項目蝉衣,會發(fā)現(xiàn)多了一個 Empty Compose Activity 的選項括尸,選擇它,各種依賴會為你配置好病毡。那么一個 hello world 會是這個樣子的:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp()
}
}
}
@Composable
fun MyApp() {
Text("Hello world!")
}
好的濒翻,你已經(jīng)知道了 Compose 的基礎知識,現(xiàn)在可以嘗試用它來構建各種界面了啦膜。
請先慢著動手有送。如果去看官網(wǎng)的入門教程,你所得到的核心內(nèi)容的確就是這樣僧家。由于文檔及教程還很不完善雀摘,這里我建議,出門左轉學習一下 Flutter八拱。
這不是一句玩笑話阵赠。Compose 在控件的使用以及組合上涯塔,跟 Flutter 更為相近,而跟 Android 之前的那套 UI 沒有太大的關系清蚀。使用 Layout Inspector 這個工具來查看 Compose 編寫的應用匕荸,可以看到 Activity 內(nèi)是一個 AndroidComposeView 包含了很多 RepaintBoundaryView。而沒有使用原先就有的 TextView 等控件枷邪。
假如你只接觸過 Android 的 UI 編程每聪,可能需要一段時間來熟悉然后轉變原先的思維。雖然 Compose 本身還很不完善齿风,但這種聲明式的編程范式(declarative programming)可以通過學習 React 或者 Flutter 等 UI 框架未雨綢繆一番药薯。
關于聲明式編程稍后再提,我還是繼續(xù)談一下一些常見的情形救斑。
布局
在布局上童本,首先會教會你使用 Column 和 Row,能夠對應代替 LinearLayout脸候;FrameLayout 也換成了 Stack穷娱。如果你對 main axis、cross axis 還比較陌生运沦,如上所說泵额,可以嘗試學習一下 React 和 Flutter。
視頻中提到携添,Compose 對 ConstraintLayout 的支持還正在進行中嫁盲。所以可以猜測還在對于現(xiàn)有的布局控件,以后應該還是會添加支持的烈掠。
列表
大多數(shù)應用都逃不了網(wǎng)絡請求 + 列表展示羞秤!
Compose 提供了 VerticalScroller 和 HorizontalScroller 來生成列表,在使用上與 RecyclerView 是完全不同的體驗:
@Composable
fun MyApp() {
VerticalScroller {
Column {
repeat(20) {
Row(mainAxisSize = LayoutSize.Expand) {
Container(height = 48.dp) {
Text("Item $it", modifier = Spacing(left = 16.dp))
}
}
}
}
}
}
事實上左敌,Scroller 與 ScrollView 更接近瘾蛋,只提供了一個滾動的功能,并沒提到有對 View 進行回收復用矫限。
Style
@Composable
fun Greeting(name: String) {
MaterialTheme {
Surface(color = Color.White) {
Text(text = "Hello $name!", style = +themeTextStyle { h5 })
}
}
}
以上代碼添加了 Material 的主題哺哼。
在我看來,這種方式比使用 XML 那套繁瑣叼风、不直觀取董、缺乏文檔的方法要好太多。當然咬扇,也可能只是因為我菜甲葬。
Live Preview
Android Studio 4.0 還提供了一個名為 Live Preview 的功能來預覽 Compose 的效果。通過給 Compose 函數(shù)添加 @Preview
注解來預覽該函數(shù)的效果懈贺。這個函數(shù)必須是無參的经窖,而且可以給注解傳遞不同的參數(shù)來預覽各種不同情況下(不同主題不同屏幕大小等)的界面坡垫。
功能是好功能,但代碼發(fā)生變動后画侣,必須要重新 build 后才能更新預覽冰悠,現(xiàn)在效率還比較低。
狀態(tài)管理
最后談談當狀態(tài)(數(shù)據(jù))發(fā)生變化后配乱,Compose 如何響應溉卓。
@Model
class State(var count: Int = 0)
@Composable
fun MyApp() {
val state = State()
MaterialTheme {
Stack {
aligned(Alignment.Center) {
Button("${state.count} clicks", {
state.count++
})
}
}
}
}
上面這段代碼,放置了一個按鈕搬泥,每按一次按鈕上的計數(shù)就會 +1桑寨。代碼里并沒有對 State 進行監(jiān)聽,然后再對 UI 進行更新忿檩。而是在 State 類上添加了 Model
注解尉尾,Compose 自動進行 UI 的更新(官方稱之為 recompose)。
你或許會想:哦燥透,setState()
沙咏。
Compose?
為什么需要 Compose
Android 已經(jīng)十年,設備的變化非常大班套,也涌現(xiàn)出很多心的開發(fā)技術和思想肢藐,但用來開發(fā) UI 的工具卻還依舊停留在十年前。不少控件已經(jīng)過時而且背負了太多歷史包袱吱韭,重新開始或許是更好的選擇吆豹。
開發(fā) UI 需要編寫 XML 布局,通過代碼加載杉女,可能還需要通過 XML 來定義 style瞻讽,為了編寫一個界面要做的工作太多了鸳吸。而且考慮到 Activity 和 Fragment熏挎,需要顧及的就更多了。如果要自定義一個 View晌砾,要做什么工作坎拐?我想“自定義 View”這幾個字,都能嚇退一批人吧养匈。
并且大量 UI 工具與系統(tǒng)的版本綁定哼勇,新功能新修復無法及時讓開發(fā)者和用戶受益。Material design呕乎?Who cares积担?那是 5.0 以上才大部分支持的東西,更別提 shape theming 了猬仁。
原先的控件使用了一些只有官方能使用的黑魔法帝璧,也就是 hidden API先誉。Compose 將完全在公開的 API 上進行構建,官方使用的廣大的普通開發(fā)者也能夠使用的烁。
插點私貨褐耳。我偶爾會看著隔壁 Flutter 流下羨慕的淚水。它提供了大量官方的控件渴庆,應對各種場景铃芦,而且在各種系統(tǒng)版本上提供統(tǒng)一的行為。而我卻需要滿世界找非官方的實現(xiàn)襟雷,一個個查看是否滿足我的需求刃滓,是否還在維護,是否需要自己魔改耸弄。當然注盈,我無比感恩這些開發(fā)者的貢獻,但我覺得我們應該被 Android 官方善待叙赚。
然后是 data flow老客。對于 UI 編程來說,分發(fā)事件和接收狀態(tài)是與開發(fā)者關聯(lián)最密切的事情震叮。而現(xiàn)有的 android.widget 在這兩個方面都做得不夠好:狀態(tài)的管理比較混亂胧砰;事件在分發(fā)時就已經(jīng)改變了控件的狀態(tài);listener 可以跟 Kotlin 結合更緊密提供更合適的做法苇瓣。
聲明式 UI 編程
聲明式編程通常是相對于命令式編程(imperative programming)來說的尉间,不關注編程中具體的過程,而是以最后的結果為重點击罪。在 UI 這一特定的領域來說哲嘲,聲明式編程則意味著:當狀態(tài)發(fā)生變化時,聲明式框架會自動更新視圖媳禁。
聲明式的 UI 框架會關注:
- 對于給定的數(shù)據(jù)眠副,UI 是如何被展示的;
- 怎么對事件進行響應竣稽;
- 不考慮 UI 之前的狀態(tài)對當前的狀態(tài)產(chǎn)生的影響囱怕。
也就是說,它只關心當前的數(shù)據(jù)(狀態(tài))會渲染出什么樣的外觀毫别,而不把數(shù)據(jù)當成一個擁有上下文的狀態(tài)流來看待娃弓。
Composition
Composition over inheritance.
組合優(yōu)于繼承。
Compose 通過組合的方式構成界面岛宦。這或許也是起名叫 Compose 的原因台丛。
遵循單一責任原則,Compose 函數(shù)都只有一個單一的目的砾肺,想實現(xiàn)某一種效果挽霉,就使用對應的 Compose 函數(shù)私恬。比如想要設置背景,則需要使用 Surface
炼吴,沒有其他 Compose 函數(shù)可以做到這一點本鸣。
@Composable
使用 @Composable
注解的函數(shù)只能在另一個的 Composable 函數(shù)中調用」璞模看到這句話荣德,你可能會想起:“ 只有替身才能打倒替身 !”
如果你對 Kotlin Coroutine 有所了解童芹,suspend 函數(shù)也同樣只能在 suspend 函數(shù)中進行調用涮瞻。而 coroutine 的實現(xiàn),是 Kotlin 編譯器在編譯時假褪,將 suspend 函數(shù)進行了改造署咽,會給函數(shù)添加一個 Continuation 參數(shù),并對函數(shù)體進行一定程度的改造生音。詳細的這里不多寫了宁否。
對于 Composable 函數(shù),同樣也是使用了 Kotlin 編譯器插件將函數(shù)變形了缀遍。Composable 函數(shù)里添加的則是 Composer慕匠,這是一個類似于 gap buffer 的數(shù)據(jù)結構。Gap buffer 在文本編輯器里被普遍使用域醇,有興趣的可以多了解一下台谊。
總結
再次強調,Compose 處于很早期的階段譬挚,API 也好锅铅,具體的底層實現(xiàn)也好,都很可能會發(fā)生變化减宣。官方的示例 JetNews 也存在一定的性能問題盐须。
所以我覺得普通開發(fā)者還不需要去了解具體使用的細節(jié)。但還是有幾個建議:
- 如果還沒學習 Kotlin蚪腋,快學吧丰歌。
- 然后考慮學習一下 Kotlin coroutine。
- 學習已經(jīng)比較成熟的聲明式 UI 框架屉凯,比如 React 和 Flutter⊙廴埽考慮了解一下它們的應用和原理悠砚,比如狀態(tài)管理的最佳實踐和 virtual DOM 等。這樣可以快速掌握同類型的 UI 框架堂飞。
擴展資料
Codelab - Jetpack Compose Basics
Declarative UI Patterns (Google I/O'19)