Jetpack Compose:利用聲明式程式設計建構Android原生界面(UI)的 工具包
更少的代碼、代碼量銳減
強大的工具/元件支援
直覺的 Kotlin API
簡單易用
聲明性程式設計範式:聲明性的函數建構一個簡單的界面元件,無需修改任何 XML 布局,也不需要使用布局編輯器,隻需要調用 Jetpack Compose 函數來聲明想要的元素,Compose 編譯器即會完成後面的所有工作
舉個栗子:簡單的可組合函數
<code>class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Text("Hello world!") } } }</code>

動态 :組合函數是用 Kotlin 而不是 XML 編寫,見上<code>$name</code> 的傳入
需要注意的事項:
可組合函數可以按任何順序執行
<code>//可以按任何順序進行,不能讓 StartScreen() 設定某個全局變量(附帶效應)并讓 MiddleScreen() 利用這項更改。相反,其中每個函數都需要保持獨立。 @Composable fun ButtonRow() { MyFancyNavigation { StartScreen() MiddleScreen() EndScreen() } }</code>
可組合函數可以并行執行
Compose 可以通過并行運作可組合函數來優化重組,這樣一來,Compose 就可以利用多個核心,并以較低的優先級運作可組合函數(不在螢幕上)
這種優化意味着,可組合函數可能會在背景線程池中執行,如果某個可組合函數對 ViewModel 調用一個函數,則 Compose 可能會同時從多個線程調用該函數
調用某個可組合函數時,調用可能發生在與調用方不同的線程上,這意味着,應避免使用修改可組合 lambda 中的變量的代碼,既因為此類代碼并非線程安全代碼,又因為它是可組合 lambda 不允許的附帶效應
重組會跳過盡可能多的 可組合函數和 lambda
重組是樂觀的操作,可能會被取消
可組合函數可能會像動畫的每一幀一樣非常頻繁地運作
已了解的同學,可直接跳過
需要更新到Arctic Fox 2020-3-1 版本以上,此版本以下Android studio 無此支援-
我們注意到此項目隻支援Kotlin 最低sdk 版本為21,Android 5.0
Gradle Compose相關依賴
由于新版本邀請java 11,安裝 java 8 環境的需要以下修複
@Preview起作用,環境正常
Android 傳統從xml-狀态的變更關系,程式員需要大量的代碼維護Ui 界面,以達到界面狀态的正确性,費時費力,即便是借助MVVM架構,一樣需要維護狀态,因為布局隻有一套
聲明式與傳統XML 實作差別,Compose 聲明式布局,是直接重建了UI,是以不會有狀态問題
Text:Compose 提供了基礎的 <code>BasicText</code> 和 <code>BasicTextField</code>,它們是用于顯示文字以及處理使用者輸入的主要函數。Compose 還提供了更進階的 <code>Text</code> 和 <code>TextField</code>
<code>Text("Hello World")</code>
重組Text->Button
<code>@Composable fun ClickCounter(clicks: Int, onClick: () -> Unit) { Button(onClick = onClick) { Text("I've been clicked $clicks times") } }</code>
Modifier可以修改控件的位置、高度、邊距、對齊方式等等
<code>//`padding` 設定各個UI的padding。padding的重載的方法一共有四個。 Modifier.padding(10.dp) // 給上下左右設定成同一個值 Modifier.padding(10.dp, 11.dp, 12.dp, 13.dp) // 分别為上下左右設值 Modifier.padding(10.dp, 11.dp) // 分别為上下和左右設值 Modifier.padding(InnerPadding(10.dp, 11.dp, 12.dp, 13.dp))// 分别為上下左右設值 //這裡設定的值必須為`Dp`,`Compose`為我們在Int中擴充了一個方法`dp`,幫我們轉換成`Dp`。 //`plus` 可以把其他的Modifier加入到目前的Modifier中。 Modifier.plus(otherModifier) // 把otherModifier的資訊加入到現有的modifier中 //`fillMaxHeight`,`fillMaxWidth`,`fillMaxSize` 類似于`match_parent`,填充整個父layout。 Modifier.fillMaxHeight() // 填充整個高度 //`width`,`heigh`,`size` 設定Content的寬度和高度。 Modifier.width(2.dp) // 設定寬度 Modifier.height(3.dp) // 設定高度 Modifier.size(4.dp, 5.dp) // 設定高度和寬度 //`widthIn`, `heightIn`, `sizeIn` 設定Content的寬度和高度的最大值和最小值。 Modifier.widthIn(2.dp) // 設定最大寬度 Modifier.heightIn(3.dp) // 設定最大高度 Modifier.sizeIn(4.dp, 5.dp, 6.dp, 7.dp) // 設定最大最小的寬度和高度 //`gravity` 在`Column`中元素的位置。 Modifier.gravity(Alignment.CenterHorizontally) // 橫向居中 Modifier.gravity(Alignment.Start) // 橫向居左 Modifier.gravity(Alignment.End) // 橫向居右 //`rtl`, `ltr` 開始布局UI的方向。 Modifier.rtl // 從右到左 //更多Modifier學習:https://developer.android.com/jetpack/compose/modifiers-list</code>
<code>Column</code> 線性布局≈ <code>Android LinearLayout-VERTICAL</code>
<code>Row</code> 水準布局≈<code>Android LinearLayout-HORIZONTAL</code>
<code>Box</code>幀布局≈<code>Android FrameLayout</code>,可将一個元素放在另一個元素上,如需在 <code>Row</code> 中設定子項的位置,請設定 <code>horizontalArrangement</code> 和 <code>verticalAlignment</code> 參數。對于 <code>Column</code>,請設定 <code>verticalArrangement</code> 和 <code>horizontalAlignment</code> 參數
相對布局,需要引入 <code>ConstraintLayout</code>
引入
<code>constraintlayout-compose</code>用法流程
完整用法示例
可以滾動的布局
但以上布局并無法實作重用,可能導緻性能問題,下面介紹我們重點布局,清單
<code>LazyColumn/LazyRow==RecylerView/listView</code> 清單布局,解決了滾動時的性能問題,<code>LazyColumn</code> 和 <code>LazyRow</code> 之間的差別就在于它們的清單項布局和滾動方向不同
内邊距
<code>LazyColumn( contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), ) { // ... }</code>
item間距
<code>LazyColumn( verticalArrangement = Arrangement.spacedBy(4.dp), ) { // ... }</code>
浮動清單的浮動标題,使用 <code>LazyColumn</code> 實作粘性标題,可以使用實驗性 <code>stickyHeader()</code>函數
<code>@OptIn(ExperimentalFoundationApi::class) @Composable fun ListWithHeader(items: List<Item>) { LazyColumn { stickyHeader { Header() } items(items) { item -> ItemRow(item) } } }</code>
網格布局<code>LazyVerticalGrid</code>
<code>@OptIn(ExperimentalFoundationApi::class) @Composable fun PhotoGrid(photos: List<Photo>) { LazyVerticalGrid( cells = GridCells.Adaptive(minSize = 128.dp) ) { items(photos) { photo -> PhotoItem(photo) } } }</code>
通過重組基礎布局實作
Canvas
<code>Canvas(modifier = Modifier.fillMaxSize()) { val canvasWidth = size.width val canvasHeight = size.height drawCircle( color = Color.Blue, center = Offset(x = canvasWidth / 2, y = canvasHeight / 2), radius = size.minDimension / 4 ) } //drawCircle 畫圓 //drawRectangle 畫矩形 //drawLine //畫線</code>
動畫Api 選擇
導航欄
<code>implementation("androidx.navigation:navigation-compose:2.4.0-alpha05")</code>
Compose總體來說,對于Android-Native布局實作上更加簡單高效,值得大家一學
Compose 寫法與Flutter-Dart 有高度類似的情況,後面我們可以做一篇與Flutter-Dart 語音寫布局的一些對比