Jetpack Compose 提供材質設計這項實作內容,是一個可用來建立數位介面的全方位設計系統。Material Design 元件 (按鈕、資訊卡、切換按鈕等) 是以 材質主題設定為基礎建構而成,能夠以系統化的方式自訂 Material Design,以更切合您產品品牌的方式呈現。材質主題包含顏色、字體排版和形狀屬性。自訂這些屬性時,變更會自動反映在您建構應用程式的元件中。
Jetpack Compose 可透過 MaterialTheme
可組合項實作這些概念:
MaterialTheme(
colors = …,
typography = …,
shapes = …
) {
// app content
}
設定要傳遞給 MaterialTheme
的參數,用來設定應用程式的主題。
圖 1. 第一張螢幕截圖顯示未設定 MaterialTheme
的應用程式,因此採用預設樣式。第二張螢幕截圖顯示會將參數傳遞至 MaterialTheme
以自訂樣式的應用程式。
顏色
顏色是在 Compose 中使用 Color
類別 (簡單的 data-holding 類別) 建立型式。
val Red = Color(0xffff0000)
val Blue = Color(red = 0f, green = 0f, blue = 1f)
雖然您可以依照自己的偏好整理這些資料 (例如頂層常數、單例模式內部或定義內嵌),我們仍強烈建議您在主題中指定色彩,並從該處擷取色彩。這個方法可讓您輕鬆支援深色主題和巢狀主題。
圖 2. 材質色彩系統。
Compose 提供 Colors
類別,以建立材質色彩系統的型式。Colors
提供建構工具函式來建立一組淺色或深色顏色:
private val Yellow200 = Color(0xffffeb46)
private val Blue200 = Color(0xff91a4fc)
// ...
private val DarkColors = darkColors(
primary = Yellow200,
secondary = Blue200,
// ...
)
private val LightColors = lightColors(
primary = Yellow500,
primaryVariant = Yellow400,
secondary = Blue700,
// ...
)
定義 Colors
後,您可以將其傳遞至 MaterialTheme
:
MaterialTheme(
colors = if (darkTheme) DarkColors else LightColors
) {
// app content
}
使用主題顏色
您可以使用 MaterialTheme.colors
擷取提供給 MaterialTheme
可組合項的 Colors
。
Text(
text = "Hello theming",
color = MaterialTheme.colors.primary
)
介面和內容顏色
許多元件都接受一組顏色和內容顏色:
Surface(
color = MaterialTheme.colors.surface,
contentColor = contentColorFor(color),
// ...
TopAppBar(
backgroundColor = MaterialTheme.colors.primarySurface,
contentColor = contentColorFor(backgroundColor),
// ...
這不僅可讓您設定可組合項的色彩,還能為「內容」提供預設顏色 (包含在其中的可組合項)。許多可組合項預設會使用這個內容顏色。例如,Text
會根據父項內容的顏色設定顏色,而 Icon
則會使用該顏色設定色調。
圖 3. 設定不同的背景顏色會產生不同的文字和圖示顏色。
contentColorFor()
方法會擷取任何主題色彩的適當「開啟」顏色。舉例來說,如果您在 Surface
上設定 primary
背景顏色,就會使用此函式將 onPrimary
設為內容顏色。如果您設定了非主題的背景顏色,也應一併指定適當的內容顏色。請使用 LocalContentColor
從階層中的指定位置擷取目前背景偏好的內容顏色。
內容 Alpha
通常您會希望以不同的程度強調內容,做為告知重要性並提供視覺階層的方式。材質設計文字易讀性建議建議採用不同程度的透明度,以傳達不同的重要性等級。
Jetpack Compose 會透過 LocalContentAlpha
執行這項工作。您可以為這個 CompositionLocal
提供值,從而為階層指定內容 Alpha。巢狀可組合項可使用這個值,將 Alpha 處理方式套用至內容。舉例來說,根據預設,Text
和 Icon
會使用 LocalContentColor
的組合調整值,以使用 LocalContentAlpha
。Material 會指定某些標準 Alpha 值 (high
、medium
、disabled
),此型式是透過 ContentAlpha
物件建立。
// By default, both Icon & Text use the combination of LocalContentColor &
// LocalContentAlpha. De-emphasize content by setting content alpha
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(/*...*/)
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
Icon(/*...*/)
Text(/*...*/)
}
如要進一步瞭解 CompositionLocal
,請參閱使用 CompositionLocal 指南的本機範圍資料。
圖 4. 在文字上套用不同層級的強調效果,以視覺化的方式呈現資訊的層級。第一行文字是標題且最重要的資訊,因此使用 ContentAlpha.high
。第二行包含較不重要的中繼資料,因此使用 ContentAlpha.medium
。
深色主題
在 Compose 中,為 MaterialTheme
可組合項提供不同的 Colors
組合,以實作淺色和深色主題:
@Composable
fun MyTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
MaterialTheme(
colors = if (darkTheme) DarkColors else LightColors,
/*...*/
content = content
)
}
在這個範例中,MaterialTheme
會包裝在其可組合函式中,該函式接受指定是否要使用深色主題的參數。在這種情況下,函式會查詢裝置主題設定,藉此取得 darkTheme
的預設值。
您可以使用像這樣的程式碼來檢查目前的 Colors
是淺色還是深色:
val isLightTheme = MaterialTheme.colors.isLight
Icon(
painterResource(
id = if (isLightTheme) {
R.drawable.ic_sun_24dp
} else {
R.drawable.ic_moon_24dp
}
),
contentDescription = "Theme"
)
高度重疊
在 Material 中,深色主題中高度較高的途徑將取得高度重疊,以此方式調淡背景。途徑的高度越高 (提高使其更接近隱含光源),途徑顏色就會變得較淺。
使用深色時,這些重疊是由 Surface
可組合項自動套用,亦適用於使用途徑的其他材質可組合項:
Surface(
elevation = 2.dp,
color = MaterialTheme.colors.surface, // color will be adjusted for elevation
/*...*/
) { /*...*/ }
圖 5.資訊卡和底部導覽均使用 surface
顏色做為背景。由於資訊卡和底部導覽位於背景上方的不同高度層級,因此顏色會略有不同:資訊卡比背景顏色淡,底部導覽又比資訊卡更淡。
如果是不涉及 Surface
的自訂情境,請使用 LocalElevationOverlay
;也就是包含 ElevationOverlay
(由 Surface
可組合項使用) 的 CompositionLocal
:
// Elevation overlays
// Implemented in Surface (and any components that use it)
val color = MaterialTheme.colors.surface
val elevation = 4.dp
val overlaidColor = LocalElevationOverlay.current?.apply(
color, elevation
)
如要停用高度重疊,請在可組合階層中想要取消的地方給予 null
:
MyTheme {
CompositionLocalProvider(LocalElevationOverlay provides null) {
// Content without elevation overlays
}
}
受限的輔色
在多數情況下,Material 會偏好使用 surface
顏色 (而非 primary
顏色),進而為深色主題推薦套用受限的輔色。根據預設,材質可組合項 (例如 TopAppBar
和 BottomNavigation
) 會實作此行為。
圖 6. 使用受限輔色的材質深色主題。上方應用程式列採用淺色主題的主色和深色主題的途徑顏色。
針對自訂情境,請使用 primarySurface
擴充功能屬性:
Surface(
// Switches between primary in light theme and surface in dark theme
color = MaterialTheme.colors.primarySurface,
/*...*/
) { /*...*/ }
字體排版
材質會定義類型系統,並建議您少用語意命名樣式。
圖 7. 材質類型系統。
Compose 會透過 Typography
、TextStyle
和字型相關類別實作類型系統。Typography
建構函式提供每種樣式的預設值,因此您可以略過任何不想自訂的樣式:
val Rubik = FontFamily(
Font(R.font.rubik_regular),
Font(R.font.rubik_medium, FontWeight.W500),
Font(R.font.rubik_bold, FontWeight.Bold)
)
val MyTypography = Typography(
h1 = TextStyle(
fontFamily = Rubik,
fontWeight = FontWeight.W300,
fontSize = 96.sp
),
body1 = TextStyle(
fontFamily = Rubik,
fontWeight = FontWeight.W600,
fontSize = 16.sp
)
/*...*/
)
MaterialTheme(typography = MyTypography, /*...*/)
如果您想在往後使用相同的字體,請指定 defaultFontFamily parameter
並省略任何 TextStyle
元素的 fontFamily
:
val typography = Typography(defaultFontFamily = Rubik)
MaterialTheme(typography = typography, /*...*/)
使用文字樣式
您可透過 MaterialTheme.typography
存取 TextStyle
。擷取 TextStyle
,如下所示:
Text(
text = "Subtitle2 styled",
style = MaterialTheme.typography.subtitle2
)
圖 8. 使用一系列字體和樣式來代表您的品牌。
形狀
Material 定義了形狀系統,可讓您定義大型、中型和小型元件的形狀。
圖 9. 材質形狀系統。
Compose 透過 Shapes
類別實作形狀系統,讓您為每個尺寸類別指定 CornerBasedShape
:
val Shapes = Shapes(
small = RoundedCornerShape(percent = 50),
medium = RoundedCornerShape(0f),
large = CutCornerShape(
topStart = 16.dp,
topEnd = 0.dp,
bottomEnd = 0.dp,
bottomStart = 16.dp
)
)
MaterialTheme(shapes = Shapes, /*...*/)
根據預設,許多元件都會使用這些形狀。舉例來說,Button
、TextField
以及 FloatingActionButton
預設為小型;AlertDialog
預設為中型;ModalDrawer
預設為大型。詳情請參閱形狀配置參考資料中的完整對應資訊。
使用形狀
您可透過 MaterialTheme.shapes
存取 Shape
。使用下列程式碼擷取 Shape
:
Surface(
shape = MaterialTheme.shapes.medium, /*...*/
) {
/*...*/
}
圖 10. 使用形狀來代表品牌或狀態。
預設樣式
在 Compose 中沒有如同 Android 檢視畫面中預設樣式的概念。您可以自行建立包裝材質元件的「超載」可組合函式,藉此提供類似的功能。舉例來說,如要建立按鈕樣式,請將按鈕包裝在您自己的可組合函式中,這會直接設定您想要變更的參數,然後以參數形式向包含的可組合項公開其他值。
@Composable
fun MyButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit
) {
Button(
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.secondary
),
onClick = onClick,
modifier = modifier,
content = content
)
}
主題重疊
您可以透過建立 MaterialTheme
可組合項的巢狀結構,在 Compose 中進行與 Android Views 主題重疊對等的設定。由於 MaterialTheme
會將顏色、字體排版和形狀預設為目前的主題值,如果主題只設定其中一項參數,其他參數仍會使用預設值。
此外,將以檢視模式為基礎的畫面遷移至 Compose 時,請留意 android:theme
屬性的使用情形。您可能需要在 Compose UI 樹狀結構的該部分中新增 MaterialTheme
。
在 Owl 範例中,大部分畫面的詳細資料畫面使用 PinkTheme
,相關區段則使用 BlueTheme
。請參閱下方的螢幕截圖和程式碼。
圖 11. Owl 範例中的巢狀主題。
@Composable
fun DetailsScreen(/* ... */) {
PinkTheme {
// other content
RelatedSection()
}
}
@Composable
fun RelatedSection(/* ... */) {
BlueTheme {
// content
}
}
元件狀態
可互動 (已點擊、切換等) 的材質元件可以處於不同的視覺狀態。狀態包括已啟用、已停用、已按下等。
可組合項通常有 enabled
參數。設為 false
可禁止互動,並變更顏色和高度等屬性,以便透過視覺化方式傳達元件狀態。
圖 12. 有 enabled = true
(左) 和 enabled = false
(右) 的按鈕。
在大部分情況下,您可以仰賴顏色和高度等預設值。 如果您想設定不同狀態使用的值,可以使用各種類別和便利函式。請參考下方按鈕範例:
Button(
onClick = { /* ... */ },
enabled = true,
// Custom colors for different states
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.secondary,
disabledBackgroundColor = MaterialTheme.colors.onBackground
.copy(alpha = 0.2f)
.compositeOver(MaterialTheme.colors.background)
// Also contentColor and disabledContentColor
),
// Custom elevation for different states
elevation = ButtonDefaults.elevation(
defaultElevation = 8.dp,
disabledElevation = 2.dp,
// Also pressedElevation
)
) { /* ... */ }
圖 13. 包含 enabled = true
(左) 和 enabled = false
(右) 的按鈕,使用調整後的顏色和高度值。
分享關係圖
材質元件會透過分享關係圖來表示正在互動。如果您在階層中使用 MaterialTheme
,則會使用 Ripple
做為預設的 Indication
內部修飾元 (例如 clickable
和 indication
)。
在大部分情況下,您可以仰賴預設的 Ripple
。如想設定外觀,您可以使用 RippleTheme
變更顏色和 Alpha 等屬性。
您可以延伸 RippleTheme
,並採用 defaultRippleColor
和 defaultRippleAlpha
公用程式函式。然後,您可以利用 LocalRippleTheme
在階層中提供自訂分享關係圖主題:
@Composable
fun MyApp() {
MaterialTheme {
CompositionLocalProvider(
LocalRippleTheme provides SecondaryRippleTheme
) {
// App content
}
}
}
@Immutable
private object SecondaryRippleTheme : RippleTheme {
@Composable
override fun defaultColor() = RippleTheme.defaultRippleColor(
contentColor = MaterialTheme.colors.secondary,
lightTheme = MaterialTheme.colors.isLight
)
@Composable
override fun rippleAlpha() = RippleTheme.defaultRippleAlpha(
contentColor = MaterialTheme.colors.secondary,
lightTheme = MaterialTheme.colors.isLight
)
}
圖 14.經由 RippleTheme
產生不同漣漪值的按鈕。
瞭解詳情
如要進一步瞭解 Compose 中的材質主題設定,請參閱下列其他資源。