Jetpack Compose 可讓您更加輕鬆地設計和建構應用程式的使用者介面。Compose 透過下列方法將狀態轉換為 UI 元素:
- 元素組合
- 元素版面配置
- 元素繪圖
本文件的重點在於元素的版面配置,說明 Compose 提供的一些建構元素,幫助您配置 UI 元素。
Compose 的版面配置目標
版面配置系統的 Jetpack Compose 實作主要有兩個目標:
可組合函式的基本概念
可組合函式是 Compose 的基本建構元素。可組合函式是會發出 Unit
的函式,用於說明您使用者介面的某些部分。函式會接收部分輸入內容,然後產生螢幕上顯示的內容。如要瞭解更多有關可組合元件的資訊,請參閱 Compose 心理模型說明文件。
可組合函式可能會發出多個 UI 元素。不過,如果您未提供如何排列這些元素的指導方式,Compose 可能會以您不喜好的方式排列元素。例如,下列程式碼會產生兩個文字元素:
@Composable
fun ArtistCard() {
Text("Alfred Sisley")
Text("3 minutes ago")
}
如果沒有提供您希望如何排列它們的指導方式,Compose 會將文字元素彼此堆疊在一起,這使得文字元素不容易讀取:
Compose 提供一系列可立即使用的版面配置,協助您排列 UI 元素,並輕鬆定義專屬您個人、更加專業的版面配置。
標準版面配置元件
在許多情況下,您都可以使用 Compose 的標準版面配置元素。
使用
Column
將項目垂直地放置在螢幕上。
@Composable
fun ArtistCard() {
Column {
Text("Alfred Sisley")
Text("3 minutes ago")
}
}
同樣地,使用 Row
將項目水平地放置在螢幕上。Column
和 Row
都支援設定它們所包含的元素對齊方式。
@Composable
fun ArtistCard(artist: Artist) {
Row(verticalAlignment = Alignment.CenterVertically) {
Image(/*...*/)
Column {
Text(artist.name)
Text(artist.lastSeenOnline)
}
}
}
使用 Box
即可將元素置於另一個元素的上方。Box
也支援設定其所包含的元素的具體對齊方式。
@Composable
fun ArtistAvatar(artist: Artist) {
Box {
Image(/*...*/)
Icon(/*...*/)
}
}
通常只需要這些建構元素即可。您可以編寫自己的可組合函式,將這些版面配置合併成更適合的版面配置,以搭配您的應用程式。
若要在 Row
中設定子項位置,請設定 horizontalArrangement
和 verticalAlignment
引數。針對 Column
,設定 verticalArrangement
和 horizontalAlignment
引數:
@Composable
fun ArtistCard(artist: Artist) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
Image(/*...*/)
Column { /*...*/ }
}
}
版面配置型式
在版面配置型式中,UI 樹狀結構是以單一傳遞方式進行配置。系統會要求每個節點先自行測量,接著以遞迴方式測量任何子項,將大小限制傳遞至樹狀結構下方的子項。接下來,系統會設定分葉節點的大小和位置,並將已解析的大小和位置指示傳遞回樹狀結構上方。
簡單地說,父項測量會在子項測量之前,但是尺寸與位置的設定是在子項之後。
請考慮使用下列 SearchResult
函式。
@Composable
fun SearchResult(...) {
Row(...) {
Image(...)
Column(...) {
Text(...)
Text(..)
}
}
}
此函式會產生以下 UI 樹狀結構,
SearchResult
Row
Image
Column
Text
Text
在 SearchResult
範例中,UI 樹狀結構的版面配置會依照以下順序:
- 系統會要求根節點
Row
進行測量。 - 根節點
Row
會要求第一個子項Image
進行測量。 Image
是分葉節點 (也就是沒有任何子項),因此會報告大小並傳回位置指示。- 根節點
Row
會要求第二個子項Column
進行測量。 Column
節點會要求其第一個Text
子項進行測量。- 第一個
Text
節點是分葉節點,因此會報告大小並傳回位置指示。 Column
節點會要求其第二個Text
子項進行測量。- 第二個
Text
節點是分葉節點,因此會報告大小並傳回位置指示。 - 既然已經測量
Column
節點、設定其子項大小與位置,因此它可以決定自己的大小和位置。 - 既然已經測量根節點
Row
、設定其子項大小與位置,因此它可以決定自己的大小和位置。
效能
Compose 僅會測量子項一次,藉此達到高度效能。單一行程 測量功能非常適合效能,可讓 Compose 有效率地處理深度 UI 樹狀結構。如果某個元素測量其子項兩次,而該子項又測量其每個子項兩次 (以此類推),那麼每次嘗試配置整個 UI 都需要花費許多工作,進而難以掌握應用程式效能。
如果版面配置因為某些原因需要多次測量,Compose 會提供特殊系統,也就是內建函式測量。如要閱讀更多有關此功能的資訊,請參閱 Compose 版面配置中的內建函式測量。
由於測量和位置是版面配置傳遞的明顯子階段,因此僅會影響項目位置 (而不是測量) 的任何變更都會個別執行。
在版面配置中使用輔助鍵
如同 Compose 輔助鍵中所述,您可以使用輔助鍵來裝飾或增加可組合元件。輔助鍵是自訂版面配置時所不可少的。舉例來說,我們會鏈結數個輔助鍵以自訂 ArtistCard
:
@Composable
fun ArtistCard(
artist: Artist,
onClick: () -> Unit
) {
val padding = 16.dp
Column(
Modifier
.clickable(onClick = onClick)
.padding(padding)
.fillMaxWidth()
) {
Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
Spacer(Modifier.size(padding))
Card(elevation = 4.dp) { /*...*/ }
}
}
在上面的程式碼中,注意共同使用的不同輔助鍵函式。
clickable
可對於使用者輸入內容產生可組合回應,並顯示漣漪。padding
會在元素周圍放置空格。fillMaxWidth
提供可組合元件填入透過父項提供的寬度上限。size()
會指定元素的偏好寬度和高度。
可捲動的版面配置
如需瞭解更多有關可捲動版面配置的資訊,請參閱 Compose 手勢說明文件。
如需關於清單和惰性清單的資訊,請參閱 Compose 清單說明文件。
回應式版面配置
版面配置在設計時應考量不同的螢幕方向和板型規格大小。Compose 提供一些現成的機制, 可協助您配合各種螢幕設定調整可組合的版面配置。
限制
為了瞭解父項的限制,並據此設計版面配置,您可以使用 BoxWithConstraints
。測量限制位於內容 lambda 的範圍內。您可以使用這些測量限制,針對不同的螢幕設定撰寫不同的版面配置:
@Composable
fun WithConstraintsComposable() {
BoxWithConstraints {
Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
}
}
基於位置的版面配置
Compose 提供許多根據質感設計建立,並且包含 androidx.compose.material:material
依附元件的可組合元件 (在 Android Studio 中建立 Compose 專案時已包括),可讓您輕鬆地建立 UI。例如
Drawer
、
FloatingActionButton
和 TopAppBar
之類的元素也皆有提供。
材質元件會大量使用位置 API,Compose 會採用這個模式將自訂層帶入可組合元件的頂端。這種方法使得元件更具彈性,因為元件接受的是可自行設定的子項元素,而不需要揭露子項的所有設定參數。位置會在 UI 中保留空白,讓開發人員依據需要填入。舉例來說,您可以在 TopAppBar
中自訂下列位置:
可組合元件通常採用 content
可組合的 lambda ( content: @Composable
() -> Unit
)。位置 API 會公開特定用途的多個 content
參數。舉例來說,TopAppBar
可讓您提供 title
、navigationIcon
和 actions
的內容。
舉例來說,
Scaffold
可讓您使用基本的質感設計版面配置結構來實作使用者介面。
Scaffold
為最常用頂層材質元件提供位置,
例如 TopAppBar
、
BottomAppBar
、
FloatingActionButton
和 Drawer
。藉由使用
Scaffold
,即可輕鬆確保這些元件的位置正確
並且正常運作。
@Composable
fun HomeScreen(/*...*/) {
Scaffold(
drawerContent = { /*...*/ },
topBar = { /*...*/ },
content = { /*...*/ }
)
}