安卓 第六章:主题、样式与国际化

本章深入 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) ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐

👉 下一章:第七章------性能优化

相关推荐
Ehtan_Zheng2 小时前
7个Kotlin Delegate
android
用户69371750013842 小时前
2026 Android 开发,现在还能入行吗?
android·前端·ai编程
YBZha2 小时前
Android Camera2 + OpenGL 竖屏或横屏预览会有“轻微拉伸”
android
seabirdssss3 小时前
Appium 在小米平板上的安装受限与闪退排查
android·appium·电脑
喂_balabala3 小时前
Kotlin-属性委托
android·开发语言·kotlin
空中海3 小时前
第一章:Android 系统架构与核心原理
android·系统架构
lI-_-Il4 小时前
适配工具箱:手机里的全能数字瑞士军刀
android·音视频
彳亍走的猪4 小时前
Android 全局防抖/防重复点击
android·java·开发语言
程序员陆业聪4 小时前
Android图片加载框架深度对比:Coil 3.4.0 vs Glide 5.0,该选哪个?
android