Jetpack Compose自定义主题未在模拟器显示,求助排查
问题:Jetpack Compose自定义主题不生效,按钮显示青色而非紫色
已将自定义Jetpack Compose主题应用到Activity,但在模拟器运行时,按钮未显示主题指定的紫色样式,反而呈现青色。

相关代码如下:
Color.kt
import androidx.compose.ui.graphics.Color val Purple80 = Color(0xFFD0BCFF) val PurpleGrey80 = Color(0xFFCCC2DC) val Pink80 = Color(0xFFEFB8C8) val Purple40 = Color(0xFF6650a4) val PurpleGrey40 = Color(0xFF625b71) val Pink40 = Color(0xFF7D5260)
Theme.kt
private val DarkColorScheme = darkColorScheme( primary = Purple80, secondary = PurpleGrey80, tertiary = Pink80 ) private val LightColorScheme = lightColorScheme( primary = Purple40, secondary = PurpleGrey40, tertiary = Pink40 /* Other default colors to override background = Color(0xFFFFFBFE), surface = Color(0xFFFFFBFE), onPrimary = Color.White, onSecondary = Color.White, onTertiary = Color.White, onBackground = Color(0xFF1C1B1F), onSurface = Color(0xFF1C1B1F), */ ) @Composable fun MorseComposeAppTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on 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 = Typography, content = content ) }
MainActivity.kt
class MainActivity : ComponentActivity() { @OptIn(ExperimentalMaterial3Api::class) @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val toner = ToneGenerator(AudioManager.STREAM_ALARM, ToneGenerator.MAX_VOLUME) val dotTime = 200 val dashTime = 3 * dotTime setContent { MorseComposeAppTheme { Scaffold( topBar = { TopAppBar(title = { Text("Morse Code") }) } ) { Column(modifier = Modifier.fillMaxHeight()) { Box( modifier = Modifier .weight(2f) .padding(all = 2.dp) ) { SoundButton(label = "Dot") { toner.startTone(ToneGenerator.TONE_SUP_DIAL, dotTime) } } Box( modifier = Modifier .weight(1f) .padding(all = 2.dp) ) { SoundButton(label = "Dash") { toner.startTone(ToneGenerator.TONE_SUP_DIAL, dashTime) } } } } } } } } @Preview(showBackground = true) @Composable fun SoundButtonPreview() { MorseComposeAppTheme { SoundButton("Text") {} } } @Composable fun SoundButton(label: String, onClick: () -> Unit) { Button( modifier = Modifier.fillMaxSize(), onClick = onClick, ) { Text( text = label, ) } }
问题原因
这不是模拟器问题,而是主题逻辑的优先级问题:
你的MorseComposeAppTheme中dynamicColor默认值为true,且当设备系统版本为Android 12(API 31)及以上时,会优先使用系统的动态颜色方案(dynamicLightColorScheme/dynamicDarkColorScheme),这套方案会根据系统主题色生成颜色,覆盖了你自定义的LightColorScheme和DarkColorScheme,所以按钮显示的是系统动态颜色中的青色,而非你定义的紫色。
解决方法
有两种常见解决方案:
方案1:关闭动态颜色
直接将MorseComposeAppTheme的dynamicColor默认参数改为false,强制使用自定义颜色方案:
@Composable fun MorseComposeAppTheme( darkTheme: Boolean = isSystemInDarkTheme(), // 关闭动态颜色,默认使用自定义方案 dynamicColor: Boolean = false, content: @Composable () -> Unit ) { // 剩余逻辑保持不变 }
方案2:在动态颜色基础上覆盖自定义颜色
如果你想保留动态颜色的适配性,同时替换特定颜色(比如primary),可以在生成动态颜色时覆盖对应值:
val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) { dynamicDarkColorScheme(context).copy( primary = Purple80, secondary = PurpleGrey80, tertiary = Pink80 ) } else { dynamicLightColorScheme(context).copy( primary = Purple40, secondary = PurpleGrey40, tertiary = Pink40 ) } } darkTheme -> DarkColorScheme else -> LightColorScheme }
内容的提问来源于stack exchange,提问作者Amanda_C
相关产品推荐
相关产品推荐

