首页 / Android 入门教程 / Compose 主题与样式

Android 入门教程

Compose 主题与样式

本教程共 100 篇 · 第 50 篇 · 更新于 2026-07-28 · 约 8 分钟阅读

AndroidAndroid 入门教程Jetpack ComposeMaterialTheme主题暗色模式动态颜色

50. Compose 主题与样式

本节目标:理解 Compose 的主题系统,学会自定义颜色、字体、形状,实现暗色主题切换和 Android 12+ 动态颜色。

MaterialTheme 三要素

Compose 用 MaterialTheme 统一管理视觉风格。它包含三个核心维度:

  • ColorScheme(颜色方案):背景色、文字色、主色等
  • Typography(字体排版):各种文字样式
  • Shapes(形状):组件圆角

打个比方:主题像装修方案。颜色方案是墙漆和家具配色,字体排版是各房间门牌大小,形状是门窗的圆角风格。改一个主题,整个房子风格统一变化。

基本用法

@Composable
fun MyAppTheme(content: @Composable () -> Unit) {
    MaterialTheme(
        colorScheme = lightColorScheme(),
        typography = Typography(),
        shapes = Shapes(),
        content = content
    )
}

在应用内通过 MaterialTheme.colorSchemeMaterialTheme.typographyMaterialTheme.shapes 访问。

ColorScheme 颜色方案

Material 3 定义了一组语义化颜色角色:

private val LightColors = lightColorScheme(
    primary = Color(0xFF0066CC),
    onPrimary = Color.White,
    primaryContainer = Color(0xFFD1E4FF),
    onPrimaryContainer = Color(0xFF001D36),
    secondary = Color(0xFF535F70),
    background = Color(0xFFFDFCFF),
    onBackground = Color(0xFF1A1C1E),
    surface = Color(0xFFFDFCFF),
    onSurface = Color(0xFF1A1C1E),
    error = Color(0xFFBA1A1A)
)

private val DarkColors = darkColorScheme(
    primary = Color(0xFF9ECAFF),
    onPrimary = Color(0xFF003258),
    background = Color(0xFF1A1C1E),
    onBackground = Color(0xFFE2E2E5),
    surface = Color(0xFF1A1C1E),
    onSurface = Color(0xFFE2E2E5),
    error = Color(0xFFFFB4AB)
)

关键颜色角色:

颜色含义
primary / onPrimary主色 / 主色上的文字色
primaryContainer / onPrimaryContainer主色容器 / 容器内文字
background / onBackground背景色 / 背景上文字
surface / onSurface表面色 / 表面文字
error / onError错误色
Note
  • 每个颜色都有配对的 on 颜色。primary 是按钮背景,onPrimary 是按钮文字。配对使用才能保证对比度足够。

使用颜色

Text(
    "文字",
    color = MaterialTheme.colorScheme.onSurface
)

Button(onClick = { }) {
    Text("按钮")  // 自动用 onPrimary 色
}

Typography 字体排版

定义不同文字级别样式:

val AppTypography = Typography(
    headlineLarge = TextStyle(
        fontSize = 32.sp,
        fontWeight = FontWeight.Bold,
        lineHeight = 40.sp
    ),
    titleLarge = TextStyle(
        fontSize = 22.sp,
        fontWeight = FontWeight.SemiBold,
        lineHeight = 28.sp
    ),
    bodyLarge = TextStyle(
        fontSize = 16.sp,
        fontWeight = FontWeight.Normal,
        lineHeight = 24.sp
    ),
    bodyMedium = TextStyle(
        fontSize = 14.sp,
        lineHeight = 20.sp
    ),
    labelLarge = TextStyle(
        fontSize = 14.sp,
        fontWeight = FontWeight.Medium
    )
)

使用:

Text(
    "标题",
    style = MaterialTheme.typography.titleLarge
)

Text(
    "正文内容",
    style = MaterialTheme.typography.bodyMedium
)
Tip
  • 别到处硬编码 fontSizecolor。用 MaterialTheme.typography 统一管理,改一处全局生效,风格一致。

Shapes 形状

val AppShapes = Shapes(
    small = RoundedCornerShape(4.dp),
    medium = RoundedCornerShape(8.dp),
    large = RoundedCornerShape(16.dp)
)

组件自动按尺寸用对应的形状:小按钮用 small,卡片用 medium,底部弹层用 large

Card(shape = MaterialTheme.shapes.medium) { /* ... */ }

暗色主题

根据系统设置切换明暗主题:

@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colors = if (darkTheme) DarkColors else LightColors
    MaterialTheme(
        colorScheme = colors,
        typography = AppTypography,
        shapes = AppShapes,
        content = content
    )
}

isSystemInDarkTheme() 自动读取系统暗色模式设置。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                AppContent()
            }
        }
    }
}
Note
  • 只要你的界面都用了 MaterialTheme.colorScheme 的颜色,明暗主题切换是自动的—不需要给每个组件写两套颜色。

动态颜色(Dynamic Color)

Android 12+ 支持动态颜色,系统根据壁纸自动生成配色:

@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    val context = LocalContext.current
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            if (darkTheme) dynamicDarkColorScheme(context)
            else dynamicLightColorScheme(context)
        }
        darkTheme -> DarkColors
        else -> LightColors
    }

    MaterialTheme(
        colorScheme = colorScheme,
        content = content
    )
}
Tip
  • 动态颜色让应用融入用户壁纸的色调,视觉更统一。Android 12+ 默认开启,低版本回退到你定义的固定配色。这是 Material You 的核心特性。

状态栏颜色

@Composable
fun MyAppTheme(content: @Composable () -> Unit) {
    val darkTheme = isSystemInDarkTheme()
    val systemUiController = rememberSystemUiController()

    SideEffect {
        systemUiController.setSystemBarsColor(
            color = Color.Transparent,
            darkIcons = !darkTheme
        )
    }

    MaterialTheme(
        colorScheme = if (darkTheme) DarkColors else LightColors,
        content = content
    )
}
Warning
  • rememberSystemUiController() 来自 accompanist-systemuicontroller 库。较新版本 Compose 可以直接用 WindowCompat 设置状态栏外观。

完整主题文件

实际项目里,主题通常拆成单独文件 ui/theme/Theme.ktColor.ktType.kt

// Color.kt
val md_primary = Color(0xFF0066CC)
val md_onPrimary = Color.White

// Type.kt
val AppTypography = Typography(
    bodyLarge = TextStyle(fontSize = 16.sp)
)

// Theme.kt
@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) { /* 如上 */ }

小结

MaterialTheme 管理颜色、字体、形状三要素。用语义化颜色角色(primary/onPrimary/surface)而非硬编码颜色,暗色主题自动适配。Android 12+ 开启动态颜色融入系统壁纸。主题统一管理,改一处全局生效。下一节学习动画。