本章深入 Android Material Design 3 主题系统、动态颜色、暗色模式、Compose 主题定制、多语言国际化与 RTL 布局适配。
📋 章节目录
| 节 | 主题 |
|---|---|
| 6.1 | Material Design 3 主题系统 |
| 6.2 | 动态颜色(Dynamic Color / Monet) |
| 6.3 | 暗色模式适配 |
| 6.4 | Compose 主题定制与扩展 |
| 6.5 | 国际化(多语言) |
| 6.6 | RTL 布局适配 |
6.1 Material Design 3 主题系统
ColorScheme(颜色系统)
Material 3 使用基于色调调色板的颜色角色体系(Color Roles):
Primary → 主色(按钮、重要控件)
On Primary → Primary 上的内容颜色
Primary Container → 主色容器(浅色背景)
On Primary Container → Primary Container 上的内容颜色
Secondary → 辅助色
Tertiary → 第三色
Surface → 表面(Card、Sheet 背景)
Surface Variant → 变体表面
On Surface → Surface 上的内容颜色
On Surface Variant → 次要内容颜色
Background → 页面背景
Error → 错误色
Outline → 边框色
kotlin
// res/values/colors.xml(通过 Material Theme Builder 生成)
// 推荐工具:https://m3.material.io/theme-builder
// Compose 主题定义(完整版)
private val LightColorScheme = lightColorScheme(
primary = Color(0xFF6750A4),
onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFFEADDFF),
onPrimaryContainer = Color(0xFF21005D),
secondary = Color(0xFF625B71),
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = Color(0xFFE8DEF8),
onSecondaryContainer = Color(0xFF1D192B),
tertiary = Color(0xFF7D5260),
onTertiary = Color(0xFFFFFFFF),
tertiaryContainer = Color(0xFFFFD8E4),
onTertiaryContainer = Color(0xFF31111D),
error = Color(0xFFB3261E),
onError = Color(0xFFFFFFFF),
errorContainer = Color(0xFFF9DEDC),
onErrorContainer = Color(0xFF410E0B),
background = Color(0xFFFFFBFE),
onBackground = Color(0xFF1C1B1F),
surface = Color(0xFFFFFBFE),
onSurface = Color(0xFF1C1B1F),
surfaceVariant = Color(0xFFE7E0EC),
onSurfaceVariant = Color(0xFF49454F),
outline = Color(0xFF79747E),
outlineVariant = Color(0xFFCAC4D0),
inverseSurface = Color(0xFF313033),
inverseOnSurface = Color(0xFFF4EFF4),
inversePrimary = Color(0xFFD0BCFF),
surfaceTint = Color(0xFF6750A4),
surfaceDim = Color(0xFFDED8E1),
surfaceBright = Color(0xFFFFFBFE),
surfaceContainerLowest = Color(0xFFFFFFFF),
surfaceContainerLow = Color(0xFFF7F2FA),
surfaceContainer = Color(0xFFF3EDF7),
surfaceContainerHigh = Color(0xFFECE6F0),
surfaceContainerHighest = Color(0xFFE6E0E9)
)
private val DarkColorScheme = darkColorScheme(
primary = Color(0xFFD0BCFF),
onPrimary = Color(0xFF381E72),
primaryContainer = Color(0xFF4F378B),
onPrimaryContainer = Color(0xFFEADDFF),
secondary = Color(0xFFCCC2DC),
onSecondary = Color(0xFF332D41),
secondaryContainer = Color(0xFF4A4458),
onSecondaryContainer = Color(0xFFE8DEF8),
tertiary = Color(0xFFEFB8C8),
onTertiary = Color(0xFF492532),
tertiaryContainer = Color(0xFF633B48),
onTertiaryContainer = Color(0xFFFFD8E4),
error = Color(0xFFF2B8B5),
onError = Color(0xFF601410),
errorContainer = Color(0xFF8C1D18),
onErrorContainer = Color(0xFFF9DEDC),
background = Color(0xFF1C1B1F),
onBackground = Color(0xFFE6E1E5),
surface = Color(0xFF1C1B1F),
onSurface = Color(0xFFE6E1E5),
surfaceVariant = Color(0xFF49454F),
onSurfaceVariant = Color(0xFFCAC4D0),
outline = Color(0xFF938F99),
outlineVariant = Color(0xFF49454F),
inverseSurface = Color(0xFFE6E1E5),
inverseOnSurface = Color(0xFF313033),
inversePrimary = Color(0xFF6750A4)
)
// Typography(字体系统)
val AppTypography = Typography(
displayLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 57.sp,
lineHeight = 64.sp,
letterSpacing = (-0.25).sp
),
displayMedium = TextStyle(fontSize = 45.sp, lineHeight = 52.sp),
displaySmall = TextStyle(fontSize = 36.sp, lineHeight = 44.sp),
headlineLarge = TextStyle(fontSize = 32.sp, fontWeight = FontWeight.SemiBold, lineHeight = 40.sp),
headlineMedium = TextStyle(fontSize = 28.sp, lineHeight = 36.sp),
headlineSmall = TextStyle(fontSize = 24.sp, lineHeight = 32.sp),
titleLarge = TextStyle(fontSize = 22.sp, fontWeight = FontWeight.Normal, lineHeight = 28.sp),
titleMedium = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Medium, lineHeight = 24.sp),
titleSmall = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Medium, lineHeight = 20.sp),
bodyLarge = TextStyle(fontSize = 16.sp, lineHeight = 24.sp, letterSpacing = 0.5.sp),
bodyMedium = TextStyle(fontSize = 14.sp, lineHeight = 20.sp, letterSpacing = 0.25.sp),
bodySmall = TextStyle(fontSize = 12.sp, lineHeight = 16.sp, letterSpacing = 0.4.sp),
labelLarge = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Medium, lineHeight = 20.sp),
labelMedium = TextStyle(fontSize = 12.sp, fontWeight = FontWeight.Medium, lineHeight = 16.sp),
labelSmall = TextStyle(fontSize = 11.sp, fontWeight = FontWeight.Medium, lineHeight = 16.sp)
)
// Shape(圆角系统)
val AppShapes = Shapes(
extraSmall = RoundedCornerShape(4.dp),
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(12.dp),
large = RoundedCornerShape(16.dp),
extraLarge = RoundedCornerShape(28.dp)
)
6.2 动态颜色(Dynamic Color / Monet)
Android 12+ 支持根据用户壁纸自动生成颜色调色板(Monet 算法)。
kotlin
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// 动态颜色(Android 12+)
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
// 动态颜色(优先)
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context)
else dynamicLightColorScheme(context)
}
// 静态颜色(兜底)
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
// 状态栏颜色适配
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = AppTypography,
shapes = AppShapes,
content = content
)
}
壁纸颜色提取(Android 12+)
kotlin
@RequiresApi(Build.VERSION_CODES.S)
fun extractWallpaperColors(context: Context): WallpaperColors? {
val wallpaperManager = WallpaperManager.getInstance(context)
return wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
}
// 手动使用 Monet 颜色的示例
@RequiresApi(Build.VERSION_CODES.S)
@Composable
fun MonetColorDemo() {
val context = LocalContext.current
val dynamicScheme = dynamicLightColorScheme(context)
Column(modifier = Modifier.padding(16.dp)) {
Text("动态颜色展示(基于壁纸)", style = MaterialTheme.typography.titleMedium)
Spacer(Modifier.height(12.dp))
val colorPairs = listOf(
"Primary" to dynamicScheme.primary,
"Secondary" to dynamicScheme.secondary,
"Tertiary" to dynamicScheme.tertiary,
"Surface" to dynamicScheme.surface,
"PrimaryContainer" to dynamicScheme.primaryContainer
)
colorPairs.forEach { (name, color) ->
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier.size(40.dp)
.background(color, RoundedCornerShape(8.dp))
)
Spacer(Modifier.width(12.dp))
Text(name)
Spacer(Modifier.weight(1f))
Text(
"#${color.toArgb().and(0xFFFFFF).toString(16).uppercase().padStart(6, '0')}",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
}
6.3 暗色模式适配
Compose 中完整的暗色模式处理
kotlin
// 主题状态管理
enum class ThemeMode { LIGHT, DARK, SYSTEM }
class ThemeViewModel @Inject constructor(
private val preferencesDataStore: UserPreferencesDataStore
) : ViewModel() {
val themeMode: StateFlow<ThemeMode> = preferencesDataStore.themeFlow
.map { themeName ->
ThemeMode.values().firstOrNull { it.name.lowercase() == themeName }
?: ThemeMode.SYSTEM
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), ThemeMode.SYSTEM)
fun setTheme(mode: ThemeMode) {
viewModelScope.launch {
preferencesDataStore.setTheme(mode.name.lowercase())
}
}
}
// 根 Composable
@Composable
fun App(themeViewModel: ThemeViewModel = hiltViewModel()) {
val themeMode by themeViewModel.themeMode.collectAsStateWithLifecycle()
val darkTheme = when (themeMode) {
ThemeMode.LIGHT -> false
ThemeMode.DARK -> true
ThemeMode.SYSTEM -> isSystemInDarkTheme()
}
AppTheme(darkTheme = darkTheme) {
AppNavHost()
}
}
// 主题切换设置页
@Composable
fun ThemeSettingSection(
currentTheme: ThemeMode,
onThemeChange: (ThemeMode) -> Unit
) {
Column(modifier = Modifier.padding(16.dp)) {
Text("外观", style = MaterialTheme.typography.titleMedium)
Spacer(Modifier.height(8.dp))
ThemeMode.values().forEach { mode ->
val label = when (mode) {
ThemeMode.LIGHT -> "浅色"
ThemeMode.DARK -> "深色"
ThemeMode.SYSTEM -> "跟随系统"
}
Row(
modifier = Modifier.fillMaxWidth().clickable { onThemeChange(mode) }
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selected = currentTheme == mode, onClick = { onThemeChange(mode) })
Spacer(Modifier.width(8.dp))
Text(label)
}
}
}
}
传统 View 系统暗色适配
xml
<!-- res/values/colors.xml(浅色)-->
<resources>
<color name="background">#FFFBFE</color>
<color name="surface">#FFFFFF</color>
<color name="text_primary">#1C1B1F</color>
<color name="text_secondary">#49454F</color>
<color name="divider">#CAC4D0</color>
</resources>
<!-- res/values-night/colors.xml(深色)-->
<resources>
<color name="background">#1C1B1F</color>
<color name="surface">#2B2930</color>
<color name="text_primary">#E6E1E5</color>
<color name="text_secondary">#CAC4D0</color>
<color name="divider">#49454F</color>
</resources>
kotlin
// 在代码中检测当前模式
fun isDarkMode(context: Context): Boolean {
return context.resources.configuration.uiMode and
Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
}
// 根据主题加载不同资源
val iconResId = if (isDarkMode(context)) R.drawable.ic_logo_dark else R.drawable.ic_logo_light
6.4 Compose 主题定制与扩展
自定义 LocalCompositionProvider(扩展主题)
kotlin
// 扩展颜色集(超出 Material 3 标准色)
data class ExtendedColors(
val success: Color,
val onSuccess: Color,
val successContainer: Color,
val warning: Color,
val onWarning: Color,
val warningContainer: Color,
val info: Color,
val onInfo: Color
)
val LocalExtendedColors = staticCompositionLocalOf<ExtendedColors> {
error("No ExtendedColors provided")
}
// 扩展 MaterialTheme 的便捷访问
val MaterialTheme.extendedColors: ExtendedColors
@Composable
get() = LocalExtendedColors.current
// 扩展间距系统
data class Spacing(
val extraSmall: Dp = 4.dp,
val small: Dp = 8.dp,
val medium: Dp = 16.dp,
val large: Dp = 24.dp,
val extraLarge: Dp = 32.dp,
val xxl: Dp = 48.dp
)
val LocalSpacing = staticCompositionLocalOf { Spacing() }
val MaterialTheme.spacing: Spacing @Composable get() = LocalSpacing.current
// 完整自定义主题
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val extendedColors = if (darkTheme) {
ExtendedColors(
success = Color(0xFF4CAF50),
onSuccess = Color.White,
successContainer = Color(0xFF1B5E20),
warning = Color(0xFFFF9800),
onWarning = Color.Black,
warningContainer = Color(0xFFE65100),
info = Color(0xFF2196F3),
onInfo = Color.White
)
} else {
ExtendedColors(
success = Color(0xFF388E3C),
onSuccess = Color.White,
successContainer = Color(0xFFC8E6C9),
warning = Color(0xFFF57C00),
onWarning = Color.White,
warningContainer = Color(0xFFFFF3E0),
info = Color(0xFF1976D2),
onInfo = Color.White
)
}
CompositionLocalProvider(
LocalExtendedColors provides extendedColors,
LocalSpacing provides Spacing()
) {
MaterialTheme(
colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme,
typography = AppTypography,
shapes = AppShapes,
content = content
)
}
}
// 使用扩展主题
@Composable
fun StatusBadge(status: OrderStatus) {
val (color, text) = when (status) {
OrderStatus.SUCCESS -> MaterialTheme.extendedColors.success to "已完成"
OrderStatus.PENDING -> MaterialTheme.extendedColors.warning to "待支付"
OrderStatus.FAILED -> MaterialTheme.colorScheme.error to "已失败"
OrderStatus.PROCESSING -> MaterialTheme.extendedColors.info to "处理中"
}
Surface(color = color, shape = RoundedCornerShape(4.dp)) {
Text(
text = text,
modifier = Modifier.padding(horizontal = MaterialTheme.spacing.small, vertical = 2.dp),
color = Color.White,
style = MaterialTheme.typography.labelSmall
)
}
}
enum class OrderStatus { SUCCESS, PENDING, FAILED, PROCESSING }
6.5 国际化(多语言)
字符串资源配置
res/
├── values/strings.xml (默认/英语)
├── values-zh/strings.xml (简体中文)
├── values-zh-rTW/strings.xml (繁体中文-台湾)
├── values-ja/strings.xml (日语)
├── values-ar/strings.xml (阿拉伯语 - RTL)
└── values-b+es+419/strings.xml (拉丁美洲西班牙语)
xml
<!-- res/values/strings.xml(英语,默认)-->
<resources>
<string name="app_name">My App</string>
<string name="greeting">Hello, %s!</string>
<string name="cart_item_count">%d item(s) in cart</string>
<string name="product_price">$%.2f</string>
<!-- 复数形式 -->
<plurals name="items_selected">
<item quantity="one">%d item selected</item>
<item quantity="other">%d items selected</item>
</plurals>
</resources>
<!-- res/values-zh/strings.xml(简体中文)-->
<resources>
<string name="app_name">我的应用</string>
<string name="greeting">你好,%s!</string>
<string name="cart_item_count">购物车有 %d 件商品</string>
<string name="product_price">¥%.2f</string>
<plurals name="items_selected">
<item quantity="other">已选 %d 件</item>
</plurals>
</resources>
kotlin
// 在 Kotlin 代码中使用
val greeting = context.getString(R.string.greeting, "张三")
val price = context.getString(R.string.product_price, 99.0)
val itemCount = resources.getQuantityString(R.plurals.items_selected, count, count)
// Compose 中使用
@Composable
fun LocalizedText() {
val count = 3
Column {
Text(stringResource(R.string.app_name))
Text(stringResource(R.string.greeting, "张三"))
Text(pluralStringResource(R.plurals.items_selected, count, count))
}
}
动态语言切换(Android 13+)
kotlin
// Android 13+ 应用级语言切换
class LanguageManager @Inject constructor(
@ApplicationContext private val context: Context
) {
fun setLanguage(languageTag: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Android 13+ 新 API(推荐)
context.getSystemService(LocaleManager::class.java)
.applicationLocales = LocaleList.forLanguageTags(languageTag)
} else {
// 旧版方式:重建 Activity
val locale = Locale(languageTag)
Locale.setDefault(locale)
val config = context.resources.configuration
config.setLocale(locale)
context.resources.updateConfiguration(config, context.resources.displayMetrics)
}
}
fun getCurrentLanguage(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.getSystemService(LocaleManager::class.java)
.applicationLocales.toLanguageTags()
} else {
Locale.getDefault().language
}
}
}
// 语言选择 UI
@Composable
fun LanguageSelector(languageManager: LanguageManager) {
val languages = listOf(
"zh" to "简体中文",
"zh-TW" to "繁體中文",
"en" to "English",
"ja" to "日本語"
)
var currentLanguage by remember { mutableStateOf(languageManager.getCurrentLanguage()) }
Column(Modifier.padding(16.dp)) {
Text("语言 / Language", style = MaterialTheme.typography.titleMedium)
Spacer(Modifier.height(8.dp))
languages.forEach { (code, name) ->
Row(
modifier = Modifier.fillMaxWidth().clickable {
languageManager.setLanguage(code)
currentLanguage = code
}.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selected = currentLanguage.startsWith(code), onClick = {
languageManager.setLanguage(code)
currentLanguage = code
})
Spacer(Modifier.width(8.dp))
Text(name)
}
}
}
}
格式化(日期/货币/数字)
kotlin
object LocaleUtils {
// 货币格式
fun formatCurrency(amount: Double, locale: Locale = Locale.getDefault()): String {
return NumberFormat.getCurrencyInstance(locale).format(amount)
}
// 日期格式
fun formatDate(date: Date, locale: Locale = Locale.getDefault()): String {
return DateFormat.getDateInstance(DateFormat.MEDIUM, locale).format(date)
}
// 数字格式(千分位)
fun formatNumber(number: Long, locale: Locale = Locale.getDefault()): String {
return NumberFormat.getNumberInstance(locale).format(number)
}
// 相对时间
fun formatRelativeTime(timestamp: Long, context: Context): String {
return DateUtils.getRelativeTimeSpanString(
timestamp,
System.currentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS
).toString()
}
}
// Compose 中的本地化格式
@Composable
fun PriceText(amount: Double) {
val locale = LocalConfiguration.current.locales[0]
val formattedPrice = remember(amount, locale) {
NumberFormat.getCurrencyInstance(locale).format(amount)
}
Text(formattedPrice, style = MaterialTheme.typography.titleMedium)
}
6.6 RTL 布局适配
从阿拉伯语、希伯来语等从右向左(RTL)语言的适配规范:
kotlin
// ✅ 使用 start/end 替代 left/right
// ❌ 错误
Modifier.padding(left = 16.dp, right = 8.dp)
// ✅ 正确(自动适配 RTL)
Modifier.padding(start = 16.dp, end = 8.dp)
// ✅ Row 在 RTL 中会自动反向
Row {
Icon(Icons.Default.ArrowForward, ...) // RTL 中自动变为 ArrowBack
Text("下一步")
}
// 检测当前布局方向
@Composable
fun DirectionAwareContent() {
val layoutDirection = LocalLayoutDirection.current
val isRTL = layoutDirection == LayoutDirection.Rtl
Row(
horizontalArrangement = if (isRTL) Arrangement.End else Arrangement.Start
) {
// 内容
}
}
// 强制指定布局方向
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
// 此区域内强制 RTL
ArabicContent()
}
// 镜像图标(箭头方向)
Icon(
imageVector = Icons.AutoMirrored.Default.ArrowForward,
contentDescription = null
)
xml
<!-- AndroidManifest.xml 启用 RTL 支持 -->
<application android:supportsRtl="true">
xml
<!-- 在 XML 布局中 -->
<!-- ❌ 错误 -->
<TextView
android:layout_marginLeft="16dp"
android:drawableLeft="@drawable/ic_icon" />
<!-- ✅ 正确 -->
<TextView
android:layout_marginStart="16dp"
android:drawableStart="@drawable/ic_icon" />
Demo 代码:chapter06
kotlin
// chapter06/ThemeDemo.kt
package com.example.androiddemos.chapter06
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@Composable
fun Chapter06ThemeDemo() {
var isDark by remember { mutableStateOf(false) }
val colorScheme = if (isDark) darkColorScheme(
primary = Color(0xFFD0BCFF),
secondary = Color(0xFFCCC2DC),
surface = Color(0xFF2B2930),
background = Color(0xFF1C1B1F),
onBackground = Color(0xFFE6E1E5),
onSurface = Color(0xFFE6E1E5)
) else lightColorScheme(
primary = Color(0xFF6750A4),
secondary = Color(0xFF625B71),
surface = Color(0xFFFFFFFF),
background = Color(0xFFFFFBFE),
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F)
)
MaterialTheme(colorScheme = colorScheme) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
LazyColumn(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
Text("主题与样式 Demo", style = MaterialTheme.typography.headlineMedium)
}
item { ThemeToggleCard(isDark = isDark, onToggle = { isDark = !isDark }) }
item { ColorPaletteCard() }
item { TypographyCard() }
item { ComponentsCard() }
}
}
}
}
@Composable
private fun ThemeToggleCard(isDark: Boolean, onToggle: () -> Unit) {
Card(modifier = Modifier.fillMaxWidth()) {
Row(
modifier = Modifier.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("深色模式", style = MaterialTheme.typography.titleMedium)
Switch(checked = isDark, onCheckedChange = { onToggle() })
}
}
}
@Composable
private fun ColorPaletteCard() {
Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text("颜色系统", style = MaterialTheme.typography.titleMedium)
Spacer(Modifier.height(12.dp))
val colorRoles = listOf(
"Primary" to MaterialTheme.colorScheme.primary,
"Secondary" to MaterialTheme.colorScheme.secondary,
"Surface" to MaterialTheme.colorScheme.surface,
"Error" to MaterialTheme.colorScheme.error
)
colorRoles.chunked(2).forEach { row ->
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
row.forEach { (name, color) ->
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier.fillMaxWidth().height(48.dp)
.background(color, RoundedCornerShape(8.dp))
)
Spacer(Modifier.height(4.dp))
Text(name, style = MaterialTheme.typography.labelSmall)
}
}
}
Spacer(Modifier.height(8.dp))
}
}
}
}
@Composable
private fun TypographyCard() {
Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text("字体系统", style = MaterialTheme.typography.titleMedium)
Spacer(Modifier.height(8.dp))
Text("Display Large", style = MaterialTheme.typography.displayLarge.copy(fontSize = MaterialTheme.typography.displayLarge.fontSize * 0.5f))
Text("Headline Medium", style = MaterialTheme.typography.headlineMedium)
Text("Title Large", style = MaterialTheme.typography.titleLarge)
Text("Body Large - 正文大", style = MaterialTheme.typography.bodyLarge)
Text("Body Medium - 正文中", style = MaterialTheme.typography.bodyMedium)
Text("Label Small - 标签小", style = MaterialTheme.typography.labelSmall)
}
}
}
@Composable
private fun ComponentsCard() {
Card(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("组件示例", style = MaterialTheme.typography.titleMedium)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(onClick = {}) { Text("Filled") }
OutlinedButton(onClick = {}) { Text("Outlined") }
TextButton(onClick = {}) { Text("Text") }
}
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
AssistChip(onClick = {}, label = { Text("Assist") })
FilterChip(selected = true, onClick = {}, label = { Text("Filter") })
}
OutlinedTextField(value = "示例文本", onValueChange = {}, label = { Text("输入框") })
}
}
}
章节总结
| 知识点 | 必掌握程度 | 面试频率 |
|---|---|---|
| Material 3 ColorScheme 角色 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 动态颜色(Dynamic Color) | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 暗色模式适配 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Compose 主题定制 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| CompositionLocal 扩展 | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 国际化字符串资源 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 动态语言切换 | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| RTL 适配(start/end) | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
👉 下一章:第七章------性能优化