主題
主要學(xué)習(xí)內(nèi)容
- Material Design 入門指南以及如何針對您的品牌對其進(jìn)行自定義
- Compose 如何實(shí)現(xiàn) Material Design 系統(tǒng)
- 如何在應(yīng)用中定義和使用顏色庵佣、排版和形狀
- 如何設(shè)置組件的樣式
- 如何支持淺色主題和深色主題
在本次學(xué)習(xí)中我們將設(shè)置新聞閱讀應(yīng)用的樣式义钉,從未設(shè)置樣式的應(yīng)用入手,應(yīng)用所學(xué)的內(nèi)容來設(shè)置應(yīng)用的主題猜煮,并為設(shè)置深色主題提供支持
準(zhǔn)備工作
因?yàn)橹蟮拇a都是基于其中的項(xiàng)目進(jìn)行的蹭睡,所以還是推薦下載诸衔。同時也可以看一下Google人員對于的Compose的代碼編寫風(fēng)格
因?yàn)榇a過多且需要添加drawable資源文件尼酿,此處就不將代碼寫出來了
在解壓文件中的ThemingCodelab
目錄中存放本次學(xué)習(xí)的案例代碼
此項(xiàng)目包含 3 個主要軟件包:
-
com.codelab.theming.data
- 該軟件包包含模型類和示例數(shù)據(jù)恭理,無需修改該軟件包 -
com.codelab.theming.ui.start
- 該 示例 的起點(diǎn)抹蚀,您應(yīng)該在該軟件包中完成此 示例 中要求的所有更改 -
com.codelab.theming.ui.finish
- 該軟件包是此 示例 的最終狀態(tài)剿牺,供參考
我們可以選擇Import Project
方式進(jìn)行學(xué)習(xí),也可以通過拷貝代碼到自己項(xiàng)目中的方式
我使用的是拷貝代碼的方式环壤,可能之后跟
Import Project
方式有些區(qū)別請諒解如果是選擇拷貝代碼的方式請注意:
Empty Activity
默認(rèn)Activity
繼承androidx.appcompat.app.AppCompatActivity
晒来,而要使用 Compose 則需要Activity
繼承androidx.activity.ComponentActivity
,否則會產(chǎn)生如下異常Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
Material主題設(shè)置
Jetpack Compose 提供了 Material Design 的實(shí)現(xiàn)郑现,Material Design 是一個用于創(chuàng)建數(shù)字化界面的綜合設(shè)計(jì)體系湃崩。Material Design 組件(按鈕、卡片接箫、開關(guān)等)在Material Theme 設(shè)置的基礎(chǔ)上構(gòu)建而成攒读,一個 Material Theme由顏色、排版和形狀屬性組成
顏色
Material Theme 定義了一些從語義上命名的顏色辛友,我們可以在應(yīng)用中使用:
其中 primary
是應(yīng)用的主要顏色薄扁,secondary
用于提供強(qiáng)調(diào)色,我們可以通過這兩種顏色的設(shè)置凸顯出需要對比區(qū)域
background
和surface
兩種顏色用于在概念上駐留在"應(yīng)用表面"的組件的容器废累,也就是背景顏色
Material Design 中還定義了on
顏色邓梅,是與具名顏色產(chǎn)生明顯對比的顏色
例如:采用
surface
作為背景顏色的容器中文本應(yīng)該采用onSurface
顏色
Material 組件已配置并使用這些主題顏色。例如邑滨,FloatingActionButton
的默認(rèn)顏色為 secondary
日缨,Card
的默認(rèn)顏色為 surface
,諸如此類掖看。
排版
同樣殿遂,Material Theme還定義了一些從語義上命名的字體樣式:
雖然我們可能不會按主題來更改字體樣式诈铛,但使用 Material Design 中的字體樣式可提升應(yīng)用內(nèi)部的一致性
Material 組件已配置并使字體樣式。例如墨礁,TopAppBar
默認(rèn)使用 h6 樣式,Button
默認(rèn)使用 button
樣式耳峦,諸如此類恩静。
形狀
Material Theme 中定義了 3 個類別:小型、中型和大型組件蹲坷;每種組件都可以定義要使用的形狀驶乾,從而自定義角的樣式(切角和圓角)和大小
默認(rèn)情況下,Button
和TextField
使用小型形狀主題循签,Card
和Dialog
使用中型形狀主題级乐,Sheet
使用大型形狀主題
如需查看組件和形狀主題的完整對應(yīng)關(guān)系,請點(diǎn)擊此處
基準(zhǔn)
Material 默認(rèn)采用“基準(zhǔn)”主題县匠,即紫色的配色方案风科、Roboto 字體比例,以及以上圖片所示的略呈圓形的形狀乞旦。如果您未指定或自定義主題贼穆,組件就會使用基準(zhǔn)主題
定義主題
MaterialTheme
在 Jetpack Compose 中實(shí)現(xiàn)主題設(shè)置的核心元素是 MaterialTheme
可組合項(xiàng)。如果將此可組合項(xiàng)放在 Compose 層次結(jié)構(gòu)中兰粉,您就可以為其中的所有組件指定顏色故痊、字體和形狀的自定義設(shè)置
@Composable
fun MaterialTheme(
colors: Colors,
typography: Typography,
shapes: Shapes,
content: @Composable () -> Unit
) { ... }
我們可以使用
MaterialTheme
object
檢索傳遞到此可組合項(xiàng)的參數(shù),以公開colors
玖姑、typography
和shapes
屬性愕秫。在之后,我們將逐一進(jìn)行深入介紹
找到 Home
可組合函數(shù) - 這是應(yīng)用的主入口點(diǎn)焰络。請注意戴甩,雖然我們聲明了 MaterialTheme
,但并未指定任何參數(shù)舔琅,因此會獲得默認(rèn)的“基準(zhǔn)”樣式:
@Composable
fun Home() {
...
MaterialTheme {
Scaffold(...){...}
}
}
創(chuàng)建主題
如果需集中設(shè)置樣式等恐,官方建議創(chuàng)建自己的可組合項(xiàng),用于封裝和配置 MaterialTheme
這樣做备蚓,我們就可以在指定自己的主題自定義設(shè)置课蔬,并在多個位置(例如跨多個屏幕或
@Preview
)輕松地重復(fù)使用這些自定義設(shè)置可以根據(jù)需要創(chuàng)建多個主題可組合項(xiàng)。例如郊尝,如果您想針對應(yīng)用的不同部分支持不同的樣式
我們可以在Theme.kt
中添加一個名為StudyTheme
新可組合函數(shù)
@Composable
fun StudyTheme(content: @Composable () -> Unit) {
MaterialTheme(content = content)
}
如果使用
Import Project
二跋,在start
中沒有Theme.kt
文件,需要新建Theme.kt
文件
我們回到 Home
可組合函數(shù)流昏,并將 MaterialTheme
替換為 StudyTheme
:
@Composable
fun Home() {
...
StudyTheme {
Scaffold(...){...}
}
}
同樣在
PostItemPreview
和FeaturedPostPreview
以使用新的StudyTheme
可組合項(xiàng)來封裝其內(nèi)容扎即,以便預(yù)覽使用新的主題@Preview("Post Item") @Composable private fun PostItemPreview() { val post = remember { PostRepo.getFeaturedPost() } StudyTheme{ Surface { PostItem(post = post) } } } @Preview("Featured Post") @Composable private fun FeaturedPostPreview() { val post = remember { PostRepo.getFeaturedPost() } StudyTheme{ FeaturedPost(post = post) } }
顏色
我們要在應(yīng)用中實(shí)現(xiàn)的調(diào)色板如下所示:
Compose 中的顏色是使用 Color
類定義的吞获。借助多個構(gòu)造函數(shù),您可以將顏色指定為 ULong
谚鄙,也可以按單獨(dú)的顏色通道來指定顏色
若要從用于指定顏色的常用“#dd0d3c”格式進(jìn)行轉(zhuǎn)換各拷,請將“#”替換為“0xff”,即
Color(0xffdd0d3c)
闷营,其中“ff”表示完整的 Alpha 值
在Color.kt
中添加以下顏色:
val Red700 = Color(0xffdd0d3c)
val Red800 = Color(0xffd00036)
val Red900 = Color(0xffc20029)
在定義顏色時烤黍,我們要根據(jù)顏色值“字面意義”命名顏色,而不要“從語義上”命名顏色
例如傻盟,命名為
Red500
而不是primary
速蕊。這樣一來,我們就可以定義多個主題娘赴。例如规哲,在深色主題中或樣式設(shè)置不同的屏幕上,系統(tǒng)可能會將另一種顏色視為primary
注意導(dǎo)入 Compose 的 Color 類型是
androidx.compose.ui.graphics.Color
诽表,而不是android.graphics.Color
如果使用
Import Project
唉锌,在start
中沒有Color.kt
文件,需要新建Color.kt
文件
現(xiàn)在关顷,我們已經(jīng)定義了應(yīng)用的顏色糊秆。接下來,我們將其合并到 MaterialTheme
所需的 Colors
對象中议双,從而將特定顏色分配到 Material 的具名顏色痘番。切換回 Theme.kt
,然后添加以下代碼:
//為外部屬性平痰,不是StudyTheme中的臨時變量
private val LightColors = lightColors(
primary = Red700,
primaryVariant = Red900,
onPrimary = Color.White,
secondary = Red700,
secondaryVariant = Red900,
onSecondary = Color.White,
error = Red800
)
下面汞舱,我們要使用 lightColors
函數(shù)來構(gòu)建 Colors
,這樣即可提供合理的默認(rèn)值宗雇,讓我們不必將構(gòu)成 Material 調(diào)色板的所有顏色全都指定出來
例如昂芜,我們尚未指定
background
顏色或許多“on”顏色,我們將會使用lightColors
中的默認(rèn)值
我們在StudyTheme
中使用這些顏色:
@Composable
fun StudyTheme(content: @Composable () -> Unit) {
MaterialTheme(
content = content,
/*+*/ colors = LightColors
)
}
此時刷新預(yù)覽赔蒲,就會發(fā)現(xiàn)新的配色方案會反映在
TopAppBar
等組件中
排版
我們要在應(yīng)用中實(shí)現(xiàn)的字體樣式如下所示:
在 Compose 中泌神,我們可以定義 TextStyle
對象,以定義設(shè)置一些文本的樣式所需的信息舞虱。下面是其屬性的示例:
@Immutable
class TextStyle(
val color: Color = Color.Unspecified,
val fontSize: TextUnit = TextUnit.Unspecified,
val fontWeight: FontWeight? = null,
val fontStyle: FontStyle? = null,
val fontSynthesis: FontSynthesis? = null,
val fontFamily: FontFamily? = null,
val fontFeatureSettings: String? = null,
val letterSpacing: TextUnit = TextUnit.Unspecified,
val baselineShift: BaselineShift? = null,
val textGeometricTransform: TextGeometricTransform? = null,
val localeList: LocaleList? = null,
val background: Color = Color.Unspecified,
val textDecoration: TextDecoration? = null,
val shadow: Shadow? = null,
val textAlign: TextAlign? = null,
val textDirection: TextDirection? = null,
val lineHeight: TextUnit = TextUnit.Unspecified,
val textIndent: TextIndent? = null
){ ... }
我們所需的字體比例要針對標(biāo)題使用 Montserrat
欢际,并針對正文文本使用 Domine
相關(guān)字體文件在 示例 的
res/font
文件夾中
在Type.kt
文件中定義 FontFamily
(結(jié)合了每個 Font
的不同粗細(xì)):
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)
)
然后創(chuàng)建一個 MaterialTheme
接受的 Typography
對象,為比例中的每個語義樣式指定 TextStyle
:
val StudyTypography = 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
)
)
我們在StudyTheme
中使用新的 Typography
:
@Composable
fun StudyTheme(content: @Composable () -> Unit) {
MaterialTheme(
content = content,
colors = LightColors,
/*+*/ typography = StudyTypography
)
}
形狀
Compose 提供了 RoundedCornerShape
類和 CutCornerShape
類矾兜,可用于定義形狀主題
在 Shape.kt
损趋,并添加以下代碼:
val StudyShapes = Shapes(
small = CutCornerShape(topStart = 8.dp),
medium = CutCornerShape(topStart = 24.dp),
large = RoundedCornerShape(8.dp)
)
我們在StudyTheme
中使用新的 Shapes
:
@Composable
fun StudyTheme(content: @Composable () -> Unit) {
MaterialTheme(
content = content,
colors = LightColors,
typography = StudyTypography,
/*+*/ shapes = StudyShapes
)
}
刷新預(yù)覽,可以看見精選博文的 Card
變?yōu)樽笊锨薪切螤?/p>
深色主題
在應(yīng)用中支持深色主題不僅有助于您的應(yīng)用在用戶設(shè)備上更好地集成(從 Android 10 開始椅寺,設(shè)備上已提供全局深色主題切換開關(guān))浑槽,還有助于降低能耗以及為滿足無障礙功能需求提供支持蒋失。Material 提供了關(guān)于如何創(chuàng)建深色主題的接口
以下是我們想為深色主題實(shí)現(xiàn)的調(diào)色板:
打開 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
)
darkColors
和lightColors
一樣,當(dāng)我們沒有提供調(diào)色板顏色時提供默認(rèn)值
然后,更新 StudyTheme
:
@Composable
fun StudyTheme(
/*+*/darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
MaterialTheme(
content = content,
/*+*/ colors = if (darkTheme) DarkColors else LightColors,
typography = StudyTypography,
shapes = StudyShapes
)
}
此時,我們添加了用于判斷是否使用深色主題的新參數(shù),并將其默認(rèn)設(shè)為查詢設(shè)備的全局設(shè)置
為 FeaturedPost
可組合項(xiàng)創(chuàng)建新的預(yù)覽,此預(yù)覽能夠以深色主題顯示該可組合項(xiàng):
@Preview("Featured Post ? Dark")
@Composable
private fun FeaturedPostDarkPreview() {
val post = remember { PostRepo.getFeaturedPost() }
StudyTheme(darkTheme = true) {
FeaturedPost(post = post)
}
}
預(yù)覽效果對比
處理顏色
我們現(xiàn)在可以創(chuàng)建自己的Theme址晕,設(shè)置應(yīng)用的顏色、字體樣式唱遭、形狀浙垫。而所有的Material 組件默認(rèn)支持這些自定義屬性
例如,FloatingActionButton
可組合項(xiàng)默認(rèn)使用主題中的 secondary
顏色梧油,當(dāng)然我們可以通過為此參數(shù)指定不同的值來設(shè)置顏色:
@Composable
fun FloatingActionButton(
...
backgroundColor: Color = MaterialTheme.colors.secondary,
...
){ ... }
原色
Compose 提供了一個 Color
類苫耸。您可以在本地創(chuàng)建這些類,并將其保留在 object
等元素中:
Surface(color = Color.LightGray) {
Text(
text = "Hard coded colors don't respond to theme changes :(",
textColor = Color(0xffff00ff)
)
}
注意:在靜態(tài)聲明顏色定義時儡陨,請務(wù)必小心褪子,因?yàn)檫@些定義會導(dǎo)致更難/無法支持不同的主題(例如,淺色/深色主題)
Color
中有許多有用的方法骗村,例如 copy
嫌褪,您可以通過此方法使用不同的 alpha/red/green/blue 值來創(chuàng)建新的顏色
主題顏色
我們可以從主題中檢索顏色
Surface(color = MaterialTheme.colors.primary)
通過使用 MaterialTheme
object
,其colors
屬性會返回MaterialTheme
可組合項(xiàng)中設(shè)置的Colors
胚股。也就是說笼痛,我們只需為主題提供不同的顏色集,即可支持不同的外觀和風(fēng)格琅拌,而無需處理應(yīng)用代碼
由于主題中的每種顏色都是Color
實(shí)例缨伊,因此我們可以通過copy
派生出不同的顏色
val derivedColor = MaterialTheme.colors.onSurface.copy(alpha = 0.1f)
這種方法可以確保顏色可以在不同主題下正常顯示,無需編寫靜態(tài)顏色代碼
背景色和內(nèi)容顏色
許多組件都接受一對顏色和“內(nèi)容顏色”:
Surface(
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
...
){ ... }
TopAppBar(
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
...
){ ... }
這樣我們不僅可以設(shè)置可組合項(xiàng)的背景顏色进宝,還可以為"內(nèi)容"(即包含在內(nèi)的可組合項(xiàng))提供默認(rèn)顏色刻坊。默認(rèn)情況下,許多可組合項(xiàng)都會使用這種內(nèi)容顏色党晋,如:Text谭胚、Icon
contentColorFor
方法可以為任何主題顏色檢索適當(dāng)?shù)摹皁n”顏色,例如未玻,如果您設(shè)置 primary
背景灾而,它就會返回 onPrimary
作為內(nèi)容顏色。如果您設(shè)置非主題背景顏色深胳,則應(yīng)自行提供合理的內(nèi)容顏色
跟蹤
contentColorFor
方法最終會進(jìn)入該方法绰疤,所以如果backgroundColor
使用非主題顏色,需要提供contentColor
fun Colors.contentColorFor(backgroundColor: Color): Color { return when (backgroundColor) { primary -> onPrimary primaryVariant -> onPrimary secondary -> onSecondary secondaryVariant -> onSecondary background -> onBackground surface -> onSurface error -> onError else -> Color.Unspecified } }
我們還可以使用 LocalContentColor
CompositionLocal
來檢索與當(dāng)前背景形成對比的顏色:
如果對于
CompositionLocal
很陌生舞终,可以看看我的另一篇簡書 簡書-CompositionLocal
BottomNavigationItem(
unselectedContentColor = LocalContentColor.current
...
){ ... }
當(dāng)設(shè)置任何元素的顏色時轻庆,最好使用 Surface
來實(shí)現(xiàn)此目的癣猾,因?yàn)樗鼤O(shè)置適當(dāng)?shù)膬?nèi)容顏色 CompositionLocal
值
請慎用直接
Modifier.background
調(diào)用,這種調(diào)用不會設(shè)置適當(dāng)?shù)膬?nèi)容顏色
目前余爆,我們的 Header
組件background
始終使用 Color.LightGray
背景纷宇。這在淺色主題中看起來沒有問題,但在深色主題中蛾方,就會與背景形成高度對比像捶。而且也不會指定與背景顏色形成對比的文本顏色
//使用uiMode,設(shè)置預(yù)覽時使用深色主題
@Preview("Home",group="Home",uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun HomePreview() {
Home()
}
接下來桩砰,我們通過Surface
去解決這個問題
在 Header
可組合項(xiàng)中拓春,移除用于指定硬編碼顏色的 background
修飾符。改為將 Text
封裝在包含主題派生顏色的 Surface
中亚隅,并指定相應(yīng)內(nèi)容應(yīng)采用 primary
顏色:
@Composable
fun Header(
text: String,
modifier: Modifier = Modifier
) {
Surface(
color = MaterialTheme.colors.onSurface.copy(alpha = 0.1f),
contentColor = MaterialTheme.colors.primary,
modifier = modifier
) {
Text(
text = text,
modifier = Modifier
.fillMaxWidth()
.semantics { heading() }
.padding(horizontal = 16.dp, vertical = 8.dp)
)
}
}
內(nèi)容 Alpha 值
通常情況下硼莽,我們通過強(qiáng)調(diào)或弱化內(nèi)容來突出重點(diǎn)并體現(xiàn)出視覺上的層次感。Material Design 建議采用不同的不透明度來傳達(dá)這些不同的重要程度
Jetpack Compose 通過 LocalContentAlpha
實(shí)現(xiàn)此功能煮纵。您可以通過為此 CompositionLocal
提供一個值來為層次結(jié)構(gòu)指定內(nèi)容 Alpha 值
子可組合項(xiàng)可以使用此值懂鸵,例如 Text 和 Icon 默認(rèn)使用LocalContentColor
的顏色值 ,其中的Alpha
會調(diào)整為 LocalContentAlpha
的值
@Composable
fun Text(...){
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
}
}
...
}
@Composable
fun Icon(
...
tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
){ ... }
Material 指定了一些標(biāo)準(zhǔn) Alpha 值(high行疏、medium匆光、disabled),這些值由 ContentAlpha
對象提供
object ContentAlpha {
val high: Float
@Composable
get()=...
val medium: Float
@Composable
get()=...
val disabled: Float
@Composable
get()=...
}
請注意酿联,
MaterialTheme
默認(rèn)將LocalContentAlpha
設(shè)置為ContentAlpha.high
我們將使用內(nèi)容 Alpha 值來闡明精選博文的信息層次結(jié)構(gòu)终息。在 PostMetadata
可組合項(xiàng)中,重點(diǎn)突出元數(shù)據(jù) medium
:
@Composable
private fun PostMetadata(
post: Post,
modifier: Modifier = Modifier
) {
...
/*+*/CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(
text = text,
modifier = modifier
)
/*+*/}
}
深色主題
若要在 Compose 中實(shí)現(xiàn)深色主題货葬,我們只需提供不同的顏色集并通過主題查詢顏色即可
我們可以通過下面代碼檢測是否在淺色主題中運(yùn)行:
val isLightTheme = MaterialTheme.colors.isLight
此值由
lightColors/[darkColors
構(gòu)建器函數(shù)設(shè)置
Material Design 建議避免在深色主題中使用大面積的明亮顏色
一種常見模式是在淺色主題中將容器背景顏色設(shè)為 primary
采幌,并在深色主題中將其設(shè)為 surface
;許多組件都默認(rèn)使用此策略震桶,例如TopAppBar
和BottomNavigation
為了便于實(shí)現(xiàn)休傍,Colors
提供了 primarySurface
顏色,以準(zhǔn)確完成上述行為
@Composable
fun BottomNavigation(
...
backgroundColor: Color = MaterialTheme.colors.primarySurface,
...
){ ... }
@Composable
fun TopAppBar(
...
backgroundColor: Color = MaterialTheme.colors.primarySurface,
...
){ ... }
val Colors.primarySurface: Color get() = if (isLight) primary else surface
遵循此指南蹲姐,我們需要將AppBar
中的TopAppBar
的backgroundColor
切換為primarySurface
或移除此參數(shù)(因?yàn)榇藚?shù)為默認(rèn)設(shè)置)即可
@Composable
private fun AppBar() {
TopAppBar(
navigationIcon = {
Icon(
imageVector = Icons.Rounded.Palette,
contentDescription = null,
modifier = Modifier.padding(horizontal = 12.dp)
)
},
title = {
Text(text = stringResource(R.string.app_title))
},
//更換為primarySurface或直接刪除即可
backgroundColor = MaterialTheme.colors.primarySurface
)
}
在 Material 中磨取,如果采用的是深色主題,則高度較高 (elevation) 的 Surface 會獲得高度疊加層(其背景顏色會變淺)柴墩。在使用深色主題時忙厌,系統(tǒng)會自動實(shí)現(xiàn)此效果:
處理文本
在處理文本時,我們使用 Text
可組合項(xiàng)來顯示文本江咳,使用 TextField
和 OutlinedTextField
進(jìn)行文本輸入逢净,并使用 TextStyle
對文本應(yīng)用單一樣式。我們可以使用 AnnotatedString
對文本應(yīng)用多種樣式
和顏色一樣,用于顯示文本的 Material 組件可以獲取到主題排版自定義設(shè)置:
Button(...) {
Text("This text will use MaterialTheme.typography.button style by default")
}
不過實(shí)現(xiàn)此目的要比使用默認(rèn)參數(shù)(如在設(shè)置顏色時所看到的那樣)略微復(fù)雜一些
因?yàn)榻M件本身往往不會顯示文本爹土,而是提供槽 API甥雕,讓您能夠傳入 Text
可組合項(xiàng)。那么胀茵,組件是如何設(shè)置主題排版樣式的呢社露?
在后臺,它們使用 ProvideTextStyle
可組合項(xiàng)(本身就使用 CompositionLocal
)來設(shè)置"current"TextStyle
琼娘。如果您未提供具體的 textStyle
參數(shù)峭弟,Text
可組合項(xiàng)會默認(rèn)查詢此"current"樣式
@Composable
fun Button(
...
) {
val contentColor by colors.contentColor(enabled)
Surface(
...
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(
value = MaterialTheme.typography.button
) {
...
}
}
}
}
@Composable
fun Text(
...
style: TextStyle = LocalTextStyle.current
) { ... }
主題文本樣式
就像顏色一樣,最好從當(dāng)前主題中檢索 TextStyle
脱拼,使用一組數(shù)量少且一致的樣式瞒瘸,并使其更易于維護(hù)
MaterialTheme.typography
會檢索在 MaterialTheme
可組合項(xiàng)中設(shè)置的 Typography
實(shí)例,讓我們能夠使用自己定義的樣式:
Text(
style = MaterialTheme.typography.subtitle2
)
如果您需要自定義 TextStyle
熄浓,可以對其執(zhí)行 copy
操作并替換相關(guān)屬性挨务,或者讓 Text
可組合項(xiàng)接受大量樣式參數(shù),這些參數(shù)會疊加到任何 TextStyle
的上層:
//使用copy操作替換屬性
Text(
text = "Hello World",
style = MaterialTheme.typography.body1.copy(
background = MaterialTheme.colors.secondary
)
)
// 使用樣式參數(shù)覆蓋樣式中的值
Text(
text = "Hello World",
style = MaterialTheme.typography.subtitle2,
fontSize = 22.sp
)
在我們的應(yīng)用中玉组,許多地方都會自動應(yīng)用主題 TextStyle
,例如丁侄,TopAppBar
將其 title
的樣式設(shè)為 h6
惯雳,而 ListItem
將其主要文本和輔助文本的樣式分別設(shè)為 subtitle1
和 body2
接下來,我們要將主題排版樣式應(yīng)用于應(yīng)用的其余部分鸿摇。將 Header
設(shè)為使用 subtitle2
石景;對于 FeaturedPost
中的文本,將標(biāo)題設(shè)為 h6
拙吉,并將作者信息和PostMetadata
設(shè)為 body2
:
@Composable
fun Header(
text: String,
modifier: Modifier = Modifier
) {
Surface(
color = MaterialTheme.colors.onSurface.copy(alpha = 0.1f),
contentColor = MaterialTheme.colors.primary,
modifier = modifier
) {
Text(
text = text,
/*+*/ style = MaterialTheme.typography.subtitle2,
modifier = Modifier
.fillMaxWidth()
.semantics { heading() }
.padding(horizontal = 16.dp, vertical = 8.dp)
)
}
}
//FeaturedPost和PostMetadata代碼省略
多種樣式
如果您需要對某些文本應(yīng)用多種樣式潮孽,可以使用 AnnotatedString
類來應(yīng)用標(biāo)記,從而為一系列文本添加 SpanStyle
筷黔。您可以動態(tài)添加這些元素往史,也可以使用 DSL 語法來創(chuàng)建內(nèi)容:
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")
}
}
接下來,我們要為描述應(yīng)用中的各個博文的標(biāo)簽設(shè)置樣式佛舱。目前椎例,它們使用與元數(shù)據(jù)其余部分相同的文本樣式;我們將使用 overline
文本樣式和背景顏色來區(qū)分它們请祖。在 PostMetadata
可組合項(xiàng)中:
@Composable
private fun PostMetadata(
post: Post,
modifier: Modifier = Modifier
) {
val divider = " ? "
val tagDivider = " "
val text = buildAnnotatedString {
append(post.metadata.date)
append(divider)
append(stringResource(R.string.read_time, post.metadata.readTimeMinutes))
append(divider)
/*+*/ val tagStyle=MaterialTheme.typography.overline.toSpanStyle().copy(
/*+*/ background = MaterialTheme.colors.primary.copy(alpha = 0.1f)
/*+*/ )
post.tags.forEachIndexed { index, tag ->
if (index != 0) {
append(tagDivider)
}
/*+*/ withStyle(tagStyle){
append(" ${tag.uppercase(Locale.getDefault())} ")
/*+*/ }
}
}
...
}
處理形狀
與顏色和排版一樣订歪,如果設(shè)置形狀主題,相應(yīng)設(shè)置會反映在 Material 組件中肆捕。例如刷晋,Button
會獲取為小型組件設(shè)置的形狀:
@Composable
fun Button(
...
shape: Shape = MaterialTheme.shapes.small
...
) { ... }
與顏色一樣,Material 組件使用默認(rèn)參數(shù),我們可以直接查看組件將要使用的形狀類別眼虱,或提供替代方案喻奥。如需查看組件和形狀類別的完整對應(yīng)關(guān)系,請參閱此文檔蒙幻。
請注意映凳,有些組件會使用經(jīng)過修改的主題形狀,以適應(yīng)其上下文的要求邮破。例如诈豌,默認(rèn)情況下,
TextField
使用小型形狀主題抒和,但它會對底角應(yīng)用零邊角大薪糜妗:@Composable fun TextField( ... shape: Shape = MaterialTheme.shapes.small.copy( bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize ), ... ) { ... }
主題形狀
在創(chuàng)建自己的組件時,我們可以自行使用各種形狀摧莽;為此庙洼,需要使用接受形狀的可組合項(xiàng)或 Modifier
(例如,Surface
镊辕、Modifier.clip
油够、Modifier.background
、Modifier.border
等)
@Composable
fun UserProfile(
...
shape: Shape = MaterialTheme.shapes.medium
) {
Surface(shape = shape) {
...
}
}
接下來征懈,我們要將形狀主題添加到 PostItem
中顯示的圖片石咬;我們要對其應(yīng)用主題的 small
形狀,并使用 Modifier.clip
應(yīng)用該形狀:
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PostItem(
post: Post,
modifier: Modifier = Modifier
) {
ListItem(
modifier = modifier
.clickable { /* todo */ }
.padding(vertical = 8.dp),
icon = {
Image(
painter = painterResource(post.imageThumbId),
contentDescription = null,
/*+*/ modifier = Modifier.clip(shape = MaterialTheme.shapes.small)
)
},
text = {
Text(text = post.title)
},
secondaryText = {
PostMetadata(post)
}
)
}
組件樣式
Compose 沒有提供用于提取組件樣式(例如卖哎,Android View 樣式或 CSS 樣式)的明確方法鬼悠。由于所有 Compose 組件都是用 Kotlin 編寫的,因此還可通過其他方法來實(shí)現(xiàn)相同的目的
我們可以改為創(chuàng)建自己的自定義組件庫亏娜,并在整個應(yīng)用中使用這些組件
比如 示例 中:
@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
可組合項(xiàng)本質(zhì)上是樣式化的 Text
焕窝,可供我們在整個應(yīng)用中使用
所有組件都是由較低級別的構(gòu)建塊構(gòu)造而成的,我們可以使用同樣的構(gòu)建塊來自定義 Material 組件
例如维贺, Button
使用 ProvideTextStyle
可組合項(xiàng)為傳遞給它的內(nèi)容設(shè)置默認(rèn)文本樣式它掂。您可以使用完全相同的機(jī)制來設(shè)置自己的文本樣式