1. 簡介
在本程式碼研究室中,您將瞭解如何透過使用 Jetpack Compose 的主題設定 API,打造應用程式樣式。我們會說明如何自訂顏色、形狀和字型排版,為整個應用程式打造一致的風格,並支援淺色和深色等多個主題。
學習目標
在本程式碼研究室,您將學到:
- Material Design 的入門介紹,以及如何根據品牌需求進行自訂
- Compose 實作 Material Design 系統的方式
- 如何在應用程式中定義及使用顏色、字體排版和形狀
- 如何設定元件樣式
- 如何支援淺色和深色主題
建構目標
在本程式碼研究室中,我們會為新聞閱讀應用程式設定樣式,先從還沒有樣式的應用程式著手,然後再運用所學知識來打造應用程式主題,並支援深色主題。
變更前:未設定樣式的應用程式 | 變更後:樣式化應用程式 | 變更後:深色主題 |
必要條件
- 用過 Kotlin 語法,包括 lambda
- 瞭解 Compose 的基本知識。
- 熟悉 Compose 版面配置的基本知識,例如
Row
、Column
和Modifier
2. 開始設定
在這個步驟中,您將下載適用的程式碼,其中包含我們要設定樣式的簡易新聞閱讀器應用程式。
軟硬體需求
下載程式碼
如果您已安裝 Git,只要執行下列指令即可。如要檢查 Git 是否已安裝完成,請在終端機或指令列中輸入 git --version
,並確認該指令可正確執行。
git clone https://github.com/googlecodelabs/android-compose-codelabs.git cd android-compose-codelabs/ThemingCodelabM2
如果您沒有 Git,可以點選下方按鈕,下載這個程式碼研究室的所有程式碼:
在 Android Studio 中開啟專案,依序選取「File」>「Import Project」,然後前往 ThemingCodelabM2
目錄。
這項專案含有三個主要套件:
com.codelab.theming.data
包含模型類別和範例資料。在本程式碼研究室中,這個套件不需要編輯。com.codelab.theming.ui.start
這是此程式碼研究室的起點,您應該在這個套件中進行本程式碼研究室中的所有變更。com.codelab.theming.ui.finish
這是程式碼研究室的結束狀態,供您參考。
建立應用程式並加以執行
應用程式有 2 個執行設定,代表程式碼研究室的開始和結束狀態。選取設定並按下執行按鈕,即可將程式碼部署至裝置或模擬器。
這個應用程式也具有 Compose 版面配置預覽。在 start
或 finish
套件中瀏覽至 Home.kt
,並開啟設計檢視畫面,其中會顯示幾個預覽畫面,方便您在 UI 程式碼中快速疊代:
3. Material Design 主題設定
Jetpack Compose 提供 Material Design 這項產品,這是用於建立數位介面的全方位設計系統。Material Design 元件 (按鈕、資訊卡、切換鈕等) 是以 Material Design 主題設定為基礎建構而成,能夠以系統化的方式自訂 Material Design,更準確地反映產品品牌。Material Design 主題由顏色、字體排版 和 形狀屬性構成。自訂這些元素會自動反映在您建構應用程式的元件中。
對 Material Design 主題設定有所掌握,有助於瞭解如何為 Jetpack Compose 應用程式設定主題,以下我們將簡短說明相關概念。如果您已熟悉 Material Design 主題設定,可以選擇略過。
顏色
Material Design 定義了一些按照語意命名的顏色,可用於整個應用程式:
主要顏色為主要品牌顏色,次要顏色則用於提供強調色。您可以為對比的區域提供顏色更深/更淺的變化版本。背景和表面顏色可用於元件所在的容器,這些元件理論上位於應用程式中的「表面」。Material Design 也定義了「上方」顏色,這些顏色用於任一已命名色彩上方的內容,例如「表面」彩色容器中的文字顏色應為在「表面上方」的顏色。Material Design 元件會設為使用這些主題顏色,例如,懸浮動作按鈕預設為 secondary
,資訊卡預設為 surface
。
定義已命名顏色後,應用程式即可提供替代調色盤,例如淺色和深色主題:
我們也鼓勵您定義小型的調色盤,並在整個應用程式中採用一致的調色盤。Material Design 色彩工具可協助您挑選顏色並建立調色盤,甚至確保相應組合可供存取。
字體排版
同樣地,Material Design 會定義許多依語意命名的字體樣式:
雖然您可能不會依主題更改字體樣式,但使用字體比例調整功能可以提高應用程式的一致性。如果您提供自己的字型和其他字體自訂功能,這些設定會反映在應用程式所用的 Material Design 元件中。舉例來說,應用程式列預設使用 h6
樣式,按鈕則使用 button
。Material Design 的字體比例產生器可協助您建構字體比例。
形狀
Material Design 支援系統性地運用形狀來呈現您的品牌。可定義的類別分為 3 種:小型、中型和大型元件;而每種類別都可以定義要使用的形狀,並自訂邊角樣式 (直角或圓角) 和大小。
當您自訂形狀主題後,相應設定會反映在許多元件上。舉例來說,根據預設,按鈕和文字欄位會使用小型形狀主題,資訊卡和對話方塊使用中型形狀主題,試算表則使用大型形狀主題。如要全盤瞭解元件對應的形狀主題,請按這裡。Material Design 的形狀自訂工具可協助您產生形狀主題。
基準
Material Design 預設採用「基準」主題,也就是紫色的色彩配置、Roboto 字體比例,以及上圖所示的略微圓角形狀。如未指定或自訂主題,元件將使用基準主題。
4. 定義主題
MaterialTheme
在 Jetpack Compose 中實作主題設定的核心元素是 MaterialTheme
可組合函式。將這個可組合函式放入可組合階層中,您就能指定自訂元件中所有元件的色彩、類型和形狀。以下是在程式庫中定義這個可組合函式的方式:
@Composable
fun MaterialTheme(
colors: Colors,
typography: Typography,
shapes: Shapes,
content: @Composable () -> Unit
) { ...
您之後可以使用 MaterialTheme
object
擷取傳送至這個可組合函式的參數,以顯示 colors
、typography
和 shapes
屬性。我們稍後會進一步說明相關細節。
開啟 Home.kt
並找出 Home
可組合函式,這是應用程式的主要進入點。請注意,在宣告 MaterialTheme
時,我們不會指定任何參數,因此會接收預設的「基準」樣式:
@Composable
fun Home() {
...
MaterialTheme {
Scaffold(...
讓我們建立顏色、類型和形狀參數,以便為應用程式實作主題。
新建主題
如要集中管理樣式,建議您自行建立用於納入及設定 MaterialTheme
的可組合函式。如此一來,您就能在單一位置指定主題自訂項目,並在各處 (例如多個螢幕或 @Preview
) 輕鬆重複使用。您可以視需要建立多個主題可組合函式,例如針對應用程式的不同部分支援不同樣式。
在 com.codelab.theming.ui.start.theme
套件中,建立名為 Theme.kt
的新檔案。新增名為 JetnewsTheme
的可組合函式,接受其他可組合函式做為內容,並納入 MaterialTheme
:
@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
MaterialTheme(content = content)
}
現在切換回 Home.kt
並使用 JetnewsTheme
取代 MaterialTheme
(然後匯入):
- MaterialTheme {
+ JetnewsTheme {
...
這個畫面的 @Preview
中暫時不會有任何變更。更新 PostItemPreview
和 FeaturedPostPreview
,將其內容納入新的 JetnewsTheme
可組合函式,讓預覽畫面使用新的主題:
@Preview("Featured Post")
@Composable
private fun FeaturedPostPreview() {
val post = remember { PostRepo.getFeaturedPost() }
+ JetnewsTheme {
FeaturedPost(post = post)
+ }
}
顏色
以下是我們要在應用程式中實作的調色盤 (目前只是淺色主題的調色盤,我們很快就會回來設定,讓調色盤支援深色主題):
Compose 中的顏色是使用 Color
類別定義。藉由多個不同的建構函式,您可將顏色指定為 ULong
,或按個別的顏色管道指定。
在 theme
套件中建立新檔案 Color.kt
。請在這個檔案中新增下列顏色做為頂層公開屬性:
val Red700 = Color(0xffdd0d3c)
val Red800 = Color(0xffd00036)
val Red900 = Color(0xffc20029)
我們定義了應用程式顏色後,現在要把它們合併到 MaterialTheme
所需的 Colors
物件中,進而將特定顏色指派至 Material Design 中的已命名顏色。請切換回 Theme.kt
,並新增以下內容:
private val LightColors = lightColors(
primary = Red700,
primaryVariant = Red900,
onPrimary = Color.White,
secondary = Red700,
secondaryVariant = Red900,
onSecondary = Color.White,
error = Red800
)
此處使用 lightColors
函式建構 Colors
,這能提供合理的預設值,因此我們不必指定 Material Design 調色盤中的所有顏色。舉例來說,您可以發現我們並未指定 background
顏色或許多「上方」顏色,而是使用預設值。
現在,我們要在應用程式中使用這些顏色。只要更新 JetnewsTheme
可組合函式,就能使用新的 Colors
:
@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
MaterialTheme(
+ colors = LightColors,
content = content
)
}
開啟「Home.kt
」並重新整理預覽畫面。請注意,新的色彩配置會反映在 TopAppBar
等元件中。
字體排版
我們想要在應用程式中實作的字體比例如下:
在 Compose 中,我們可以定義 TextStyle
物件,藉此定義設定部分文字樣式所需的資訊。屬性範例:
data class TextStyle(
val color: Color = Color.Unset,
val fontSize: TextUnit = TextUnit.Inherit,
val fontWeight: FontWeight? = null,
val fontStyle: FontStyle? = null,
val fontFamily: FontFamily? = null,
val letterSpacing: TextUnit = TextUnit.Inherit,
val background: Color = Color.Unset,
val textAlign: TextAlign? = null,
val textDirection: TextDirection? = null,
val lineHeight: TextUnit = TextUnit.Inherit,
...
)
我們期望的類型調整功能使用 Montserrat 來處理標題,使用 Domine 來處理內文。相關字型檔案已新增至專案的 res/fonts
資料夾中。
在 theme
套件中建立新檔案 Typography.kt
。首先,定義 FontFamily
(結合每個 Font
的不同權重):
private val Montserrat = FontFamily(
Font(R.font.montserrat_regular),
Font(R.font.montserrat_medium, FontWeight.W500),
Font(R.font.montserrat_semibold, FontWeight.W600)
)
private val Domine = FontFamily(
Font(R.font.domine_regular),
Font(R.font.domine_bold, FontWeight.Bold)
)
現在,建立 MaterialTheme
可接受的 Typography
物件,為比例中的每個語意樣式指定 TextStyle
:
val JetnewsTypography = Typography(
h4 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W600,
fontSize = 30.sp
),
h5 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W600,
fontSize = 24.sp
),
h6 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W600,
fontSize = 20.sp
),
subtitle1 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W600,
fontSize = 16.sp
),
subtitle2 = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W500,
fontSize = 14.sp
),
body1 = TextStyle(
fontFamily = Domine,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
),
body2 = TextStyle(
fontFamily = Montserrat,
fontSize = 14.sp
),
button = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W500,
fontSize = 14.sp
),
caption = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.Normal,
fontSize = 12.sp
),
overline = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.W500,
fontSize = 12.sp
)
)
開啟 Theme.kt
並更新 JetnewsTheme
可組合函式,使用新的 Typography
:
@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
MaterialTheme(
colors = LightColors,
+ typography = JetnewsTypography,
content = content
)
}
開啟 Home.kt
並重新整理預覽畫面,查看新的字體排版效果。
形狀
我們想運用形狀,在應用程式中呈現品牌特色。具體來說,我們要在多項元素上使用直角形狀,如下所示:
Compose 提供 RoundedCornerShape
和 CutCornerShape
類別,可用來定義形狀主題。
在 theme
套件中建立新檔案 Shape.kt
,並新增下列指令:
val JetnewsShapes = Shapes(
small = CutCornerShape(topStart = 8.dp),
medium = CutCornerShape(topStart = 24.dp),
large = RoundedCornerShape(8.dp)
)
開啟 Theme.kt
並更新 JetnewsTheme
可組合函式,使用這些 Shapes
:
@Composable
fun JetnewsTheme(content: @Composable () -> Unit) {
MaterialTheme(
colors = LightColors,
typography = JetnewsTypography,
+ shapes = JetnewsShapes,
content = content
)
}
開啟 Home.kt
並重新整理預覽畫面,查看顯示精選貼文的 Card
如何反映新套用的形狀主題。
深色主題
在應用程式中支援深色主題,不僅有助於應用程式與使用者裝置整合 (從 Android 10 以上版本開始提供通用深色主題切換功能),還能降低耗電量及滿足無障礙需求。如要瞭解如何建立深色主題,請參考 Material Design 提供的設計指南。以下是我們想為深色主題實作的替代調色盤:
開啟 Color.kt
並新增下列顏色:
val Red200 = Color(0xfff297a2)
val Red300 = Color(0xffea6d7e)
現在,請開啟 Theme.kt
並新增:
private val DarkColors = darkColors(
primary = Red300,
primaryVariant = Red700,
onPrimary = Color.Black,
secondary = Red300,
onSecondary = Color.Black,
error = Red200
)
現在更新 JetnewsTheme
:
@Composable
fun JetnewsTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
MaterialTheme(
+ colors = if (darkTheme) DarkColors else LightColors,
typography = JetnewsTypography,
shapes = JetnewsShapes,
content = content
)
}
我們在這裡新增了新的參數以使用深色主題,並預設為查詢裝置以尋找 全域設定。這提供了良好的預設值,但如果想讓特定畫面永遠/永不顯示為深色,或者設定深色主題為 @Preview
,還是可以輕鬆覆寫。
開啟 Home.kt
,為 FeaturedPost
可組合函式建立新的預覽畫面,並以深色主題顯示:
@Preview("Featured Post • Dark")
@Composable
private fun FeaturedPostDarkPreview() {
val post = remember { PostRepo.getFeaturedPost() }
JetnewsTheme(darkTheme = true) {
FeaturedPost(post = post)
}
}
重新整理預覽窗格,即可查看深色主題預覽。
5. 使用色彩
在上一個步驟中,我們學到了如何自行建立主題,為應用程式設定顏色、字體樣式和形狀。所有 Material Design 元件都能直接使用這些自訂項目。舉例來說,FloatingActionButton
可組合函式預設會使用主題中的 secondary
顏色,但您可以為這個參數指定不同的值,設定替代顏色:
@Composable
fun FloatingActionButton(
backgroundColor: Color = MaterialTheme.colors.secondary,
...
) {
您不一定會使用預設設定。本節將說明如何在應用程式中使用色彩。
原始色彩
如先前所述,Compose 提供 Color
類別。您可以在本機建立這些類別,並將其存放於 object
等元素中:
Surface(color = Color.LightGray) {
Text(
text = "Hard coded colors don't respond to theme changes :(",
textColor = Color(0xffff00ff)
)
}
Color
提供多種實用的方法,例如 copy
可讓您以不同 Alpha/紅色/綠色/藍色值建立新的顏色。
主題顏色
更靈活的做法是從主題中擷取顏色:
Surface(color = MaterialTheme.colors.primary)
這裡,我們使用 MaterialTheme
object
,其中 colors
屬性會傳回 MaterialTheme
可組合函式中設定的 Colors
。也就是說,只要為主題提供不同的顏色組合,我們無需變更應用程式程式碼,就能支援不同的外觀和風格樣式。例如 AppBar
使用 primary
顏色,螢幕背景為 surface
,當我們變更主題顏色後,相關設定就會反映在這些可組合函式中:
由於主題中的每種顏色都是 Color
例項,我們也可以使用 copy
方法輕鬆「衍生」顏色:
val derivedColor = MaterialTheme.colors.onSurface.copy(alpha = 0.1f)
這裡,我們要建立 onSurface
顏色的副本,但不透明度為 10%。這種做法可確保顏色在不同主題下正常顯示,而無需以硬式編碼寫入靜態顏色。
表面和內容顏色
許多元件都接受一組顏色和「內容顏色」:
Surface(
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
...
TopAppBar(
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
...
這不僅可讓您設定可組合函式的色彩,還能為「內容」(例如其中的可組合函式) 提供預設顏色。許多可組合函式會預設使用這個內容顏色,例如 Text
顏色或 Icon
色調。contentColorFor
方法會針對任一主題顏色擷取適當的「上方」顏色。例如,如果設定了 primary
背景,此方法會傳回 onPrimary
做為內容顏色。如果您設定了非主題背景顏色,請務必自行提供合理的內容顏色。
Surface(color = MaterialTheme.colors.primary) {
Text(...) // default text color is 'onPrimary'
}
Surface(color = MaterialTheme.colors.error) {
Icon(...) // default tint is 'onError'
}
您可以使用 LocalContentColor
CompositionLocal
擷取與目前背景形成對比的顏色:
BottomNavigationItem(
unselectedContentColor = LocalContentColor.current ...
設定任何元素的顏色時,建議您使用 Surface
,這樣做可設定合適的內容顏色 CompositionLocal
值。請注意直接呼叫 Modifier.background
並不會設定適當的內容顏色。
-Row(Modifier.background(MaterialTheme.colors.primary)) {
+Surface(color = MaterialTheme.colors.primary) {
+ Row(
...
目前,Header
元件一律具有 Color.LightGray
背景。在淺色主題中看起來很正常,但與深色主題中的背景對比度高。他們也未指定特定的文字顏色,因此沿用目前可能不會與背景形成對比的顏色作為內容顏色:
接著就來解決這個問題。在 Home.kt
的 Header
可組合函式中,移除指定硬式編碼顏色的 background
修飾符。請改為將 Text
納入包含主題衍生顏色的 Surface
中,並指定內容應設為 primary
顏色:
+ Surface(
+ color = MaterialTheme.colors.onSurface.copy(alpha = 0.1f),
+ contentColor = MaterialTheme.colors.primary,
+ modifier = modifier
+ ) {
Text(
text = text,
modifier = Modifier
.fillMaxWidth()
- .background(Color.LightGray)
.padding(horizontal = 16.dp, vertical = 8.dp)
)
+ }
內容 Alpha 值
我們往往會想強調或淡化內容,藉此傳達重要資訊,並在視覺上提供不同的層次感。Material Design 建議採用不同程度的不透明度,以便傳達不同的重要性等級。
Jetpack Compose 會透過 LocalContentAlpha
執行這項工作。您可以為這個 CompositionLocal
提供值,從而為階層指定內容 Alpha 值。子項可組合函式可以使用這個值,例如:Text
和 Icon
預設會使用調整為使用 LocalContentAlpha
後的 LocalContentColor
組合。Material Design 會指定某些標準 Alpha 值 (high
、medium
、disabled
),這些值則由 ContentAlpha
物件建模。請注意,MaterialTheme
預設 LocalContentAlpha
至 ContentAlpha.high
。
// By default, both Icon & Text use the combination of LocalContentColor &
// LocalContentAlpha. De-emphasize content by setting a different content alpha
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(...)
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
Icon(...)
Text(...)
}
這樣一來,就能輕鬆採取一致的方式來傳達元件的重要性。
我們會使用內容 Alpha 值來釐清精選貼文的資訊階層。在 Home.kt
中的 PostMetadata
可組合函式,讓中繼資料 medium
強調:
+ CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(
text = text,
modifier = modifier
)
+ }
深色主題
如先前所述,如要在 Compose 中實作深色主題,只需透過該主題提供不同的顏色組合並查詢顏色即可。幾個例外狀況如下:
您可以檢查目前是否為在淺色主題中執行:
val isLightTheme = MaterialTheme.colors.isLight
這個值是由 lightColors (淺色) / darkColors (深色) 建構工具函式設定。
在 Material Design 中,深色主題中高度較高的表面將取得高度重疊 (背景會變亮)。使用深色調色盤時,系統會自動實作此效果:
Surface(
elevation = 2.dp,
color = MaterialTheme.colors.surface, // color will be adjusted for elevation
...
我們在應用程式中使用的 TopAppBar
和 Card
元件內都能看到這個自動行為;這些元件預設的高度為 4dp 和 1dp,因此使用深色主題時,背景會自動調亮,更有效地呈現此高度:
Material Design 建議避免在深色主題中使用大面積的明亮色彩。常見模式為在淺色主題中將容器設為 primary
顏色,而在深色主題中設為 surface
顏色。根據預設,許多元件都會採用這項策略,例如應用程式列和底部導覽列。為了方便實作,Colors
提供 primarySurface
顏色,提供此行為及這些元件的預設使用選項。
應用程式目前是將應用程式列設為 primary
顏色,我們可將其改為 primarySurface
,或直接移除這項參數 (因為此為預設值),這樣就能遵循這項原則。在 AppBar
可組合函式中,變更 TopAppBar
的 backgroundColor
參數:
@Composable
private fun AppBar() {
TopAppBar(
...
- backgroundColor = MaterialTheme.colors.primary
+ backgroundColor = MaterialTheme.colors.primarySurface
)
}
6. 處理文字
處理文字時,我們會使用 Text
可組合函式來顯示文字、使用 TextField
和 OutlinedTextField
來輸入文字,並透過 TextStyle
為文字套用單一樣式。我們可以使用 AnnotatedString
將多個樣式套用到文字。
我們發現有了顏色,顯示文字的 Material Design 元件會採取自訂主題字體排版:
Button(...) {
Text("This text will use MaterialTheme.typography.button style by default")
}
實現此做法會比使用有顏色的預設參數略為複雜一些。這是因為元件通常不會自行顯示文字,而是會提供版位 API,讓您傳入 Text
可組合函式。那麼,元件該如何設定主題字型樣式?基本上,它們會使用 ProvideTextStyle
可組合函式 (其本身使用 CompositionLocal
) 來設定「目前」的 TextStyle
。如未提供具體的 textStyle
參數,Text
可組合函式會預設查詢這個「目前」樣式。
例如,從 Compose 的 Button
和 Text
類別查詢:
@Composable
fun Button(
// many other parameters
content: @Composable RowScope.() -> Unit
) {
...
ProvideTextStyle(MaterialTheme.typography.button) { //set the "current" text style
...
content()
}
}
@Composable
fun Text(
// many, many parameters
style: TextStyle = LocalTextStyle.current // get the value set by ProvideTextStyle
) { ...
主題文字樣式
和使用顏色一樣,建議您從目前主題擷取 TextStyle
,鼓勵使用一致的小型樣式組合,讓樣式更易於維護。MaterialTheme.typography
會擷取 MaterialTheme
可組合函式中的 Typography
例項組合,方便您使用您定義的樣式:
Text(
style = MaterialTheme.typography.subtitle2
)
如要自訂 TextStyle
,您可以將其 copy
並覆寫屬性 (僅為 data class
) 或 Text
可組合函式接受多個重疊於任一 TextStyle
頂端的樣式參數:
Text(
text = "Hello World",
style = MaterialTheme.typography.body1.copy(
background = MaterialTheme.colors.secondary
)
)
Text(
text = "Hello World",
style = MaterialTheme.typography.subtitle2,
fontSize = 22.sp // explicit size overrides the size in the style
)
應用程式中的許多地方都會自動套用主題 TextStyle
,例如 TopAppBar
會將 title
的樣式設為 h6
,ListItem
則會將主要和次要文字的樣式分別設為 subtitle1
和 body2
。
接下來,我們要將主題字體樣式套用到應用程式的其餘部分。請將 Header
設為使用 subtitle2
,FeaturedPost
中的文字則設為使用 h6
做為標題、body2
用做作者和中繼資料:
@Composable
fun Header(...) {
...
Text(
text = text,
+ style = MaterialTheme.typography.subtitle2
多種樣式
如果您需要對某些文字套用多種樣式,可以使用 AnnotatedString
類別來套用標記,並在文字範圍中加上 SpanStyle
。您可以動態新增這些變數,或使用 DSL 語法建立內容:
val text = buildAnnotatedString {
append("This is some unstyled text\n")
withStyle(SpanStyle(color = Color.Red)) {
append("Red text\n")
}
withStyle(SpanStyle(fontSize = 24.sp)) {
append("Large text")
}
}
現在,我們要替標記設定樣式,這些標記用於描述應用程式中的每則貼文,目前採用與其餘中繼資料相同的文字樣式;我們將使用 overline
文字樣式和背景顏色來區分兩者。在 PostMetadata
可組合函式中:
+ val tagStyle = MaterialTheme.typography.overline.toSpanStyle().copy(
+ background = MaterialTheme.colors.primary.copy(alpha = 0.1f)
+ )
post.tags.forEachIndexed { index, tag ->
...
+ withStyle(tagStyle) {
append(" ${tag.toUpperCase()} ")
+ }
}
7. 使用形狀
就像顏色和字體一樣,設定形狀主題也會反映在 Material Design 元件中,例如 Button
會選用針對小型元件所設的形狀:
@Composable
fun Button( ...
shape: Shape = MaterialTheme.shapes.small
) {
和顏色一樣,Material Design 元件會使用預設參數,因此您可以直接檢查元件會使用的形狀類別,或是提供替代選項。如要查看元件與形狀類別的完整對應關係,請參閱說明文件。
請注意,部分元件會配合情境使用修改過的主題形狀。舉例來說,TextField
預設使用小型形狀主題,但會在底部角落套用零邊角:
@Composable
fun FilledTextField(
// other parameters
shape: Shape = MaterialTheme.shapes.small.copy(
bottomStart = ZeroCornerSize, // overrides small theme style
bottomEnd = ZeroCornerSize // overrides small theme style
)
) {
主題形狀
當然,在建立自己的元件時,您可以自行採用各種形狀,方法是使用可組合函式或接受各類形狀的 Modifier
,例如 Surface
、Modifier.clip
、Modifier.background
、Modifier.border
等。
@Composable
fun UserProfile(
...
shape: Shape = MaterialTheme.shapes.medium
) {
Surface(shape = shape) {
...
}
}
我們現在要在 PostItem
顯示的圖片中新增形狀主題設定;具體會套用主題的 small
形狀,並以 clip
Modifier
剪下左上角:
@Composable
fun PostItem(...) {
...
Image(
painter = painterResource(post.imageThumbId),
+ modifier = Modifier.clip(shape = MaterialTheme.shapes.small)
)
8. 元件「樣式」
Compose 並未提供明確的方法來讓我們擷取元件的樣式 (例如 Android View 樣式或 css 樣式)。由於所有 Compose 元件都是以 Kotlin 編寫而成,因此還有其他方法可以達成相同的目標。您可以改為自行建立自訂元件程式庫,並在應用程式中全面使用這些元件。
我們已經在應用程式中執行過這項措施:
@Composable
fun Header(
text: String,
modifier: Modifier = Modifier
) {
Surface(
color = MaterialTheme.colors.onSurface.copy(alpha = 0.1f),
contentColor = MaterialTheme.colors.primary,
modifier = modifier.semantics { heading() }
) {
Text(
text = text,
style = MaterialTheme.typography.subtitle2,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
)
}
}
Header
可組合函式基本上是樣式化的 Text
,可用於整個應用程式中。
我們發現所有元件都是使用較低層級的建構元素所建構,因此您可以使用相同的建構模塊來自訂Material Design 的元件。舉例來說,我們發現 Button
會使用 ProvideTextStyle
可組合函式作為傳遞的內容設定預設文字樣式。您可以按照相同的機制自行設定文字樣式:
@Composable
fun LoginButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit
) {
Button(
colors = ButtonConstants.defaultButtonColors(
backgroundColor = MaterialTheme.colors.secondary
),
onClick = onClick,
modifier = modifier
) {
ProvideTextStyle(...) { // set our own text style
content()
}
}
}
在本例中,我們會納入標準 Button
類別,並指定不同的 backgroundColor
和文字樣式等特定屬性,建立自己的 LoginButton
「樣式」。
沒有預設樣式的概念,例如自訂元件類型預設外觀的方法。同樣,為了達成此目的,您可以建立自己的元件,用來納入及自訂程式庫元件。舉例來說,假設您想自訂應用程式中所有 Button
的形狀,但不想變更小型形狀主題,進而影響其他 (非 Button
) 元件。為此,請建立自己的可組合函式,並在應用程式中全面使用:
@Composable
fun AcmeButton(
// expose Button params consumers should be able to change
) {
val acmeButtonShape: Shape = ...
Button(
shape = acmeButtonShape,
// other params
)
}
9. 恭喜
恭喜,您已成功完成本程式碼研究室,以及設定 Jetpack Compose 應用程式的風格!
您已實作 Material Design 主題,並自訂整個應用程式中使用的色彩、字體和形狀,以傳達品牌形象、提高一致性。現在可以開始支援淺色和深色主題。
後續步驟
請參閱 Compose 課程中的其他程式碼研究室:
其他資訊
範例應用程式
- 展示多個主題的 Owl
- 展示動態主題設定功能的 Jetcaster
- 展示導入自訂設計系統的 Jetsnack