Jetpack Compose 可用於實作質感設計,後者是一套用來打造數位介面的全方位設計系統。質感設計元件 (按鈕、卡片、切換按鈕等) 和版面配置 (例如 Scaffold
) 可當做可組合函式使用。
質感設計元件是可用於建立使用者介面的互動式建構元素。Compose 提供了許多可以馬上使用的現成元件。如要查看有哪些可用的元件,請參閱 Compose Material API 參考資料。
質感設計元件會使用應用程式中的 MaterialTheme
提供的值:
@Composable
fun MyApp() {
MaterialTheme {
// Material Components like Button, Card, Switch, etc.
}
}
如要進一步瞭解主題設定,請參閱「Compose 中的設計系統」指南。
內容位置
支援內部內容 (文字標籤、圖示等) 的質感設計元件通常會提供「位置」,也就是接受可組合內容的通用 lambda,以及尺寸和邊框間距等公用常數,以便支援符合質感設計規格的配置內部內容。
此處的範例為 Button
:
Button(
onClick = { /* ... */ },
// Uses ButtonDefaults.ContentPadding by default
contentPadding = PaddingValues(
start = 20.dp,
top = 12.dp,
end = 20.dp,
bottom = 12.dp
)
) {
// Inner content including an icon and a text label
Icon(
Icons.Filled.Favorite,
contentDescription = "Favorite",
modifier = Modifier.size(ButtonDefaults.IconSize)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text("Like")
}
圖 1. 使用 content
位置和預設邊框間距 (左側) 的 Button
,以及使用提供自訂 contentPadding
(右側) 的 content
位置的 Button
。
Button
具有一般 content
結尾的 lambda 位置,使用 RowScope
來配置資料列的內容可組合項。另外還有 contentPadding
參數,可在內部內容中套用邊框間距。您可以使用透過 ButtonDefaults
提供的常數或是自訂值。
另一個例子是 ExtendedFloatingActionButton
:
ExtendedFloatingActionButton(
onClick = { /* ... */ },
icon = {
Icon(
Icons.Filled.Favorite,
contentDescription = "Favorite"
)
},
text = { Text("Like") }
)
圖 2. 使用 icon
和 text
位置的 ExtendedFloatingActionButton
。
ExtendedFloatingActionButton
擁有供 icon
和 text
標籤使用的兩個位置,而不是一般的 content
lambda。儘管每個位置都支援一般的可組合內容,但這項元件能夠反映這些內部內容的配置方式。可在內部處理邊框間距、對齊方式和尺寸。
Scaffold
Compose 提供便利的版面配置,讓您將質感設計元件合併成常見的螢幕模式。例如 Scaffold
之類的可組合項會提供位置以容納各種不同的元件和其他螢幕元素。
螢幕內容
Scaffold
具有一般 content
結尾的 lambda 位置。lambda 會接收應該套用至內容根層級 (例如透過 Modifier.padding
) 的 PaddingValues
執行個體,藉此位移頂端和底部長條 (如果有的話)。
Scaffold(/* ... */) { contentPadding ->
// Screen content
Box(modifier = Modifier.padding(contentPadding)) { /* ... */ }
}
應用程式列
Scaffold
提供頂端應用程式列或底部應用程式列的位置。可組合項的位置會在內部處理。
您可以使用 topBar
位置和 TopAppBar
:
Scaffold(
topBar = {
TopAppBar { /* Top app bar content */ }
}
) {
// Screen content
}
您可以使用 bottomBar
位置和 BottomAppBar
:
Scaffold(
bottomBar = {
BottomAppBar { /* Bottom app bar content */ }
}
) {
// Screen content
}
這些位置可用於其他質感設計元件 (例如 BottomNavigation
)。您也可以使用自訂可組合項,例如查看 Owl 範例中的新手上路畫面。
懸浮動作按鈕
Scaffold
會提供懸浮動作按鈕的位置。
您可以使用 floatingActionButton
位置和 FloatingActionButton
:
Scaffold(
floatingActionButton = {
FloatingActionButton(onClick = { /* ... */ }) {
/* FAB content */
}
}
) {
// Screen content
}
FAB 可組合項的底部位置會在內部處理。您可以使用 floatingActionButtonPosition
參數調整水平位置:
Scaffold(
floatingActionButton = {
FloatingActionButton(onClick = { /* ... */ }) {
/* FAB content */
}
},
// Defaults to FabPosition.End
floatingActionButtonPosition = FabPosition.Center
) {
// Screen content
}
如果您正在使用 Scaffold
可組合項的 bottomBar
位置,則可以使用 isFloatingActionButtonDocked
參數將 FAB 與底部應用程式列重疊:
Scaffold(
floatingActionButton = {
FloatingActionButton(onClick = { /* ... */ }) {
/* FAB content */
}
},
// Defaults to false
isFloatingActionButtonDocked = true,
bottomBar = {
BottomAppBar { /* Bottom app bar content */ }
}
) {
// Screen content
}
圖 3. 使用 floatingActionButton
位置和 bottomBar
位置的 Scaffold
。isFloatingActionButtonDocked
參數已設為 false
(頂端) 和 true
(底部)。
BottomAppBar
支援使用 cutoutShape
參數的 FAB 裁剪,其可接受任何 Shape
。建議您提供固定元件所使用的相同 Shape
。舉例來說,FloatingActionButton
會使用 MaterialTheme.shapes.small
以及 50% 的邊角大小作為其 shape
參數的預設值:
Scaffold(
floatingActionButton = {
FloatingActionButton(onClick = { /* ... */ }) {
/* FAB content */
}
},
isFloatingActionButtonDocked = true,
bottomBar = {
BottomAppBar(
// Defaults to null, that is, No cutout
cutoutShape = MaterialTheme.shapes.small.copy(
CornerSize(percent = 50)
)
) {
/* Bottom app bar content */
}
}
) {
// Screen content
}
圖 4. 含有 BottomAppBar
以及固定 FloatingActionButton
的 Scaffold
。BottomAppBar
的自訂 cutoutShape
與 FloatingActionButton
所使用的 Shape
相符。
Snackbar
Scaffold
提供顯示 Snackbars 的方式。
這是透過包含 SnackbarHostState
屬性的 ScaffoldState
提供。您可以使用 rememberScaffoldState
建立應使用 scaffoldState
參數傳送至 Scaffold
的 ScaffoldState
執行個體。SnackbarHostState
提供 showSnackbar
函式的存取權。此暫停函式需要 CoroutineScope
(例如使用
rememberCoroutineScope
) 並且可以呼叫此函式以回應 UI 事件,以便顯示 Scaffold
內部的
Snackbar
。
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Show snackbar") },
onClick = {
scope.launch {
scaffoldState.snackbarHostState
.showSnackbar("Snackbar")
}
}
)
}
) {
// Screen content
}
您可以提供選擇性動作,並調整 Snackbar
的持續期間。
snackbarHostState.showSnackbar
函式可接受額外的 actionLabel
和 duration
參數,並傳回 SnackbarResult
。
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Show snackbar") },
onClick = {
scope.launch {
val result = scaffoldState.snackbarHostState
.showSnackbar(
message = "Snackbar",
actionLabel = "Action",
// Defaults to SnackbarDuration.Short
duration = SnackbarDuration.Indefinite
)
when (result) {
SnackbarResult.ActionPerformed -> {
/* Handle snackbar action performed */
}
SnackbarResult.Dismissed -> {
/* Handle snackbar dismissed */
}
}
}
}
)
}
) {
// Screen content
}
您可以提供含有 snackbarHost
參數的自訂 Snackbar
。詳情請參閱 SnackbarHost API reference docs
。
導覽匣
Scaffold
提供強制回應導覽匣的位置。可組合項的可拖曳工作表和版面配置會在內部處理。
您可以使用 drawerContent
位置,它可使用 ColumnScope
配置資料欄中導覽匣內容的可組合項:
Scaffold(
drawerContent = {
Text("Drawer title", modifier = Modifier.padding(16.dp))
Divider()
// Drawer items
}
) {
// Screen content
}
Scaffold
可接受一些額外的導覽匣參數。舉例來說,您可以使用 drawerGesturesEnabled
參數來切換導覽匣是否針對拖曳事件做出回應。
Scaffold(
drawerContent = {
// Drawer content
},
// Defaults to true
drawerGesturesEnabled = false
) {
// Screen content
}
透過 ScaffoldState
(包括應使用 scaffoldState
參數傳遞至 Scaffold
的 DrawerState
屬性),以程式輔助方式開啟與關閉導覽匣。DrawerState
提供 open
和 close
函式的存取權,以及與目前導覽匣狀態相關的屬性。這些暫停函式需要 CoroutineScope
(例如使用 rememberCoroutineScope
),才能收到呼叫以便回應 UI 事件。
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
drawerContent = {
// Drawer content
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Open or close drawer") },
onClick = {
scope.launch {
scaffoldState.drawerState.apply {
if (isClosed) open() else close()
}
}
}
)
}
) {
// Screen content
}
強制回應導覽匣
如果想要執行沒有 Scaffold
的強制回應導覽匣,可以使用 ModalDrawer
可組合項。它可接受與 Scaffold
類似的導覽匣參數。
val drawerState = rememberDrawerState(DrawerValue.Closed)
ModalDrawer(
drawerState = drawerState,
drawerContent = {
// Drawer content
}
) {
// Screen content
}
如果想要執行底部導覽匣,可以使用 BottomDrawer
可組合項:
val drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed)
BottomDrawer(
drawerState = drawerState,
drawerContent = {
// Drawer content
}
) {
// Screen content
}
底部功能表
如果想要執行標準底部功能表,可以使用 BottomSheetScaffold
可組合項。它可接受與 Scaffold
類似的參數,例如 topBar
、floatingActionButton
和 snackbarHost
。其中包括提供顯示底部功能表方式的其他參數。
您可以使用 sheetContent
運算單元,它會在資料欄中使用 ColumnScope
以配置工作表內容可組合項:
BottomSheetScaffold(
sheetContent = {
// Sheet content
}
) {
// Screen content
}
BottomSheetScaffold
可接受一些額外的工作表參數。舉例來說,您可以使用 sheetPeekHeight
參數來設定工作表的微調高度。也可以使用 sheetGesturesEnabled
參數來切換導覽匣是否針對拖曳事件做出回應。
BottomSheetScaffold(
sheetContent = {
// Sheet content
},
// Defaults to BottomSheetScaffoldDefaults.SheetPeekHeight
sheetPeekHeight = 128.dp,
// Defaults to true
sheetGesturesEnabled = false
) {
// Screen content
}
系統會透過
BottomSheetScaffoldState
(包括
BottomSheetState
屬性) 以程式輔助方式展開及收合工作表。您可以使用 rememberBottomSheetScaffoldState
建立應使用 scaffoldState
參數傳送至 BottomSheetScaffold
的 BottomSheetScaffoldState
執行個體。BottomSheetState
提供 expand
和 collapse
函式的存取權,以及與目前工作表狀態相關的屬性。這些暫停函式需要 CoroutineScope
(例如使用 rememberCoroutineScope
),並且可以呼叫這些函式以回應 UI 事件。
val scaffoldState = rememberBottomSheetScaffoldState()
val scope = rememberCoroutineScope()
BottomSheetScaffold(
scaffoldState = scaffoldState,
sheetContent = {
// Sheet content
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Expand or collapse sheet") },
onClick = {
scope.launch {
scaffoldState.bottomSheetState.apply {
if (isCollapsed) expand() else collapse()
}
}
}
)
}
) {
// Screen content
}
如果想要執行強制回應底部功能表,可以使用 ModalBottomSheetLayout
可組合項:
val sheetState = rememberModalBottomSheetState(
ModalBottomSheetValue.Hidden
)
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
// Sheet content
}
) {
// Screen content
}
背景幕
如果想要執行背景幕,可以使用 BackdropScaffold
可組合項。
BackdropScaffold(
appBar = {
// Top app bar
},
backLayerContent = {
// Back layer content
},
frontLayerContent = {
// Front layer content
}
)
BackdropScaffold
可接受一些額外的背景幕參數。舉例來說,您可以使用 peekHeight
和 headerHeight
參數設定背層的微調高度,以及前層的最低非作用高度。
您也可以透過 gesturesEnabled
參數來切換背景幕是否針對拖曳事件做出回應。
BackdropScaffold(
appBar = {
// Top app bar
},
backLayerContent = {
// Back layer content
},
frontLayerContent = {
// Front layer content
},
// Defaults to BackdropScaffoldDefaults.PeekHeight
peekHeight = 40.dp,
// Defaults to BackdropScaffoldDefaults.HeaderHeight
headerHeight = 60.dp,
// Defaults to true
gesturesEnabled = false
)
透過 BackdropScaffoldState
以程式輔助方式揭露及隱藏背景幕。您可以使用 rememberBackdropScaffoldState
建立應使用 scaffoldState
參數傳送至 BackdropScaffold
的 BackdropScaffoldState
執行個體。BackdropScaffoldState
提供 reveal
和 conceal
函式的存取權,以及與目前背景幕狀態相關的屬性。這些暫停函式需要 CoroutineScope
(例如使用 rememberCoroutineScope
),並且可以呼叫這些函式以回應 UI 事件。
val scaffoldState = rememberBackdropScaffoldState(
BackdropValue.Concealed
)
val scope = rememberCoroutineScope()
BackdropScaffold(
scaffoldState = scaffoldState,
appBar = {
TopAppBar(
title = { Text("Backdrop") },
navigationIcon = {
if (scaffoldState.isConcealed) {
IconButton(
onClick = {
scope.launch { scaffoldState.reveal() }
}
) {
Icon(
Icons.Default.Menu,
contentDescription = "Menu"
)
}
} else {
IconButton(
onClick = {
scope.launch { scaffoldState.conceal() }
}
) {
Icon(
Icons.Default.Close,
contentDescription = "Close"
)
}
}
},
elevation = 0.dp,
backgroundColor = Color.Transparent
)
},
backLayerContent = {
// Back layer content
},
frontLayerContent = {
// Front layer content
}
)