1. 簡介
在本程式碼研究室中,您將瞭解如何透過使用 Jetpack Compose 的主題設定 API 設定應用程式樣式。我們會說明如何自訂顏色、形狀和字型排版,以便在整個應用程式中一致使用,並支援淺色和深色主題等多個主題。
學習目標
在本程式碼研究室,您將學到:
- Material Design 的入門介紹,以及如何根據品牌需求進行自訂
- Compose 質感設計系統的實作方式
- 如何在應用程式中定義及使用顏色、字體排版和形狀
- 如何設定元件樣式
- 如何支援淺色和深色主題
建構目標
在本程式碼研究室中,我們會為新聞閱讀應用程式設定樣式。我們先從未設定樣式的應用程式開始做起,然後再運用所學知識來打造應用程式主題,並支援深色主題。
變更前:未設定樣式的應用程式 | 「變更後:樣式化應用程式」 | 「變更後:深色主題」 |
必要條件
- 具備 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/ThemingCodelab
如果您沒有 Git,可以點選下方按鈕,下載這個程式碼研究室的所有程式碼:
在 Android Studio 中開啟專案,依序選取「File」>「Import Project」,然後前往 ThemingCodelab
目錄。
這項專案含有三個主要套件:
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 主題設定設定有助於瞭解如何為 Jetpack Compose 應用程式設定主題,以下簡短說明相關概念。如果您已熟悉質感設計主題設定,可以選擇略過。
顏色
Material Design 定義了一些按照語意命名的顏色,可用於整個應用程式:
主要顏色為主要品牌顏色,次要顏色則用於提供重點色彩。您可以為對比的區域提供顏色更深/更淺的變化版本。背景和表面顏色可用於元件所在的容器,這些元件通常位於應用程式中的「表面」。質感設計也定義了「上方」顏色,這些顏色用於任一已命名色彩上方的內容,例如「表面」彩色容器中的文字顏色應為在「表面上方」的顏色。質感元件會設為使用這些主題顏色,例如,懸浮動作按鈕預設為 secondary
,資訊卡預設為 surface
。
定義已命名顏色後,應用程式即可提供替代調色盤,例如淺色和深色主題:
我們也鼓勵您定義小型的調色盤,並在整個應用程式中採用一致的調色盤。Material 色彩工具可協助您挑選顏色並建立調色盤,甚至確保方便存取組合。
字體排版
同樣地,質感設計會定義許多依語意命名的樣式類型:
儘管可能無法依主題採用不同的類型樣式,但使用類型縮放調整功能可以提高在應用程式中的一致性。提供自己的字型和其他類型自訂設定,其會反映在應用程式中使用的質感元件中。舉例來說,App Bars (應用程式列) 預設使用 h6
樣式,Buttons (按鈕) 使用的也是 button
。質感類型調整產生器工具可協助您建構類型大小。
形狀
Material Design 支援使用形狀有條理地呈現您的品牌。可定義 3 種類別:小型、中型和大型元件;每種類別都可以定義要使用的形狀,並自訂邊角樣式 (直角或圓角) 和大小。
自訂形狀主題會呈現在許多元件上,舉例來說,根據預設,按鈕和文字欄位會使用小型形狀主題,資訊卡和對話方塊使用中型形狀主題,試算表則使用大型形狀主題。元件的形狀主題的完整對應請見這裡。Material 形狀自訂工具可協助您產生形狀主題。
基準
質感設計會預設為「基準」主題,也就是紫色色彩配置、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
物件中,並為質感設計的已命名顏色指派特定顏色。切換回 Theme.kt
,並新增以下內容:
private val LightColors = lightColors(
primary = Red700,
primaryVariant = Red900,
onPrimary = Color.White,
secondary = Red700,
secondaryVariant = Red900,
onSecondary = Color.White,
error = Red800
)
此處使用 lightColors
函式建構 Colors
,提供合理的預設值,因此我們不必指定組成質感調色盤的所有顏色。例如請注意,我們並未指定 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 以上版本開始提供通用深色主題切換功能),還能降低耗電量和滿足無障礙需求。質感設計提供了設計指南,說明如何建立深色主題。以下是我們想為深色主題實作的替代調色盤:
開啟 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. 使用色彩
在上一個步驟中,我們瞭解如何自行建立主題,為應用程式設定顏色、類型樣式和形狀。所有質感元件都能直接使用這些自訂項目。舉例來說,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
組合。質感設計會指定某些標準 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 (深色) 建構工具函式設定。
在質感設計中,深色主題中高度較高的表面將取得高度重疊 (背景會變亮)。使用深色調色盤時,系統會自動實作以下程式碼:
Surface(
elevation = 2.dp,
color = MaterialTheme.colors.surface, // color will be adjusted for elevation
...
我們在應用程式中使用的 TopAppBar
和 Card
元件中都能看到這個自動行為;這些工具預設的高度為 4dp 和 1dp,因此使用深色主題時,背景會自動調亮,以更有效地傳達此高度:
Material Design 建議避免深色主題中出現大量明亮的色彩。常見模式為在淺色主題中加上容器 primary
顏色,在深色主題中加上 surface
顏色。根據預設,許多元件都會採用這項策略,例如 App Bar 和 Bottom Navigation。為了方便實作,Colors
提供 primarySurface
顏色,提供此行為及這些元件的預設使用選項。
應用程式目前會將 App Bar 設為 primary
顏色,但我們可將其改為 primarySurface
,或直接移除這項參數 (採用預設值),這樣就能遵循這項原則。在 AppBar
可組合項中,變更 TopAppBar
的 backgroundColor
參數:
@Composable
private fun AppBar() {
TopAppBar(
...
- backgroundColor = MaterialTheme.colors.primary
+ backgroundColor = MaterialTheme.colors.primarySurface
)
}
6. 處理文字
處理文字時,我們會使用 Text
可組合項以顯示文字、TextField
和 OutlinedTextField
以輸入文字和 TextStyle
,為文字套用單一樣式。我們可以使用 AnnotatedString
將多個樣式套用到文字。
我們發現有了顏色,顯示文字的質感元件會採取自訂主題字體排版:
Button(...) {
Text("This text will use MaterialTheme.typography.button style by default")
}
實現此做法會比使用有顏色的預設參數略為複雜一些。這是因為元件通常不會自行顯示文字,而是提供 Slot 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. 使用形狀
就像顏色和字型一樣,設定形狀主題也會反映在質感元件中,例如 Button
會挑選小型元件的形狀集:
@Composable
fun Button( ...
shape: Shape = MaterialTheme.shapes.small
) {
和色彩一樣,Material 元件會使用預設參數,因此您可以透過簡單直接的方式檢查元件會使用的形狀類別,或是提供替代選項。如需元件與形狀類別的完整對應關係,請參閱說明文件。
請注意,部分元件會配合情境使用修改過的主題形狀。舉例來說,TextField
預設使用小型形狀主題,但會在底部角落套用零邊角:
@Composable
fun FilledTextField(
// other parameters
shape: Shape = MaterialTheme.shapes.small.copy(
bottomStart = ZeroCornerSize, // overrides small theme style
bottomEnd = ZeroCornerSize // overrides small theme style
)
) {
主題形狀
當然,在使用可組合項或接受 Surface
、Modifier.clip
或 Modifier.background
、Modifier.border
等的 Modifier
建立自己的元件時,您可以自行使用形狀。
@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
,可用於整個應用程式中。
我們發現所有元件都是使用較低層級的建構元素所建構,因此您可以使用相同的建構模塊來自訂質感設計的元件。舉例來說,我們發現 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 應用程式的風格!
您已實作質感設計主題,並自訂整個應用程式中使用的色彩、字體和形狀,以傳達品牌形象、提高一致性。您已新增支援淺色和深色主題。
後續步驟
請參閱 Compose 課程中的其他程式碼研究室:
其他資訊
範例應用程式
- 展示多個主題的 Owl
- 展示動態主題設定功能的 Jetcaster
- 展示導入自訂設計系統的 Jetsnack