要实现背景、弹框、按钮、Switch 和 Checkbox 等组件在 View 和 Compose 中的完全一致,需要系统性的主题配置。以下是完整解决方案:
1. 统一主题基础配置
在 res/values/themes.xml
和 res/values-night/themes.xml
中:
<!-- 基础主题 -->
<style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight">
<!-- 背景色 -->
<item name="android:windowBackground">@color/background</item>
<item name="colorSurface">@color/surface</item>
<!-- 按钮样式 -->
<item name="materialButtonStyle">@style/Widget.MyApp.Button</item>
<!-- Switch 和 Checkbox -->
<item name="checkboxStyle">@style/Widget.MyApp.CompoundButton.CheckBox</item>
<item name="switchStyle">@style/Widget.MyApp.CompoundButton.Switch</item>
<!-- 弹框背景 -->
<item name="dialogCornerRadius">8dp</item>
<item name="dialogBackground">@drawable/bg_dialog</item>
</style>
<!-- 按钮样式 -->
<style name="Widget.MyApp.Button" parent="Widget.MaterialComponents.Button">
<item name="backgroundTint">@color/button_background</item>
<item name="cornerRadius">4dp</item>
</style>
<!-- Switch 和 Checkbox 样式 -->
<style name="Widget.MyApp.CompoundButton.CheckBox" parent="Widget.MaterialComponents.CompoundButton.CheckBox">
<item name="buttonTint">@color/checkbox_tint</item>
</style>
<style name="Widget.MyApp.CompoundButton.Switch" parent="Widget.MaterialComponents.CompoundButton.Switch">
<item name="useMaterialThemeColors">false</item>
<item name="thumbTint">@color/switch_thumb</item>
<item name="trackTint">@color/switch_track</item>
</style>
2. Compose 主题配置
@Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (darkTheme) {
// 深色主题颜色
darkColors(
primary = Color(0xFFBB86FC),
secondary = Color(0xFF03DAC6),
surface = colorResource(R.color.surface),
background = colorResource(R.color.background),
onPrimary = Color.Black,
onSecondary = Color.Black
)
} else {
// 浅色主题颜色
lightColors(
primary = Color(0xFF6200EE),
secondary = Color(0xFF03DAC6),
surface = colorResource(R.color.surface),
background = colorResource(R.color.background),
onPrimary = Color.White,
onSecondary = Color.Black
)
}
val shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(8.dp),
large = RoundedCornerShape(12.dp)
)
MaterialTheme(
colors = colors,
shapes = shapes,
typography = Typography,
content = content
)
// 统一组件样式
CompositionLocalProvider(
LocalMinimumInteractiveComponentEnforcement provides false
) {
ProvideTextStyle(
value = MaterialTheme.typography.body1,
content = content
)
}
}
3. 关键组件一致性实现
弹框背景一致
传统 View 弹框:
AlertDialog.Builder(this)
.setView(R.layout.custom_dialog)
.create()
.apply {
window?.setBackgroundDrawableResource(R.drawable.bg_dialog)
}
Compose 弹框:
@Composable
fun MyAlertDialog() {
AlertDialog(
onDismissRequest = { /*...*/ },
modifier = Modifier.background(
color = MaterialTheme.colors.surface,
shape = MaterialTheme.shapes.medium
),
properties = DialogProperties()
) {
// 弹框内容
}
}
按钮样式一致
传统 View 按钮:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View 按钮"
style="@style/Widget.MyApp.Button"/>
Compose 按钮:
Button(
onClick = { /*...*/ },
shape = MaterialTheme.shapes.small,
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary,
contentColor = MaterialTheme.colors.onPrimary
)
) {
Text("Compose 按钮")
}
Switch 和 Checkbox 一致
传统 View:
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.MyApp.CompoundButton.CheckBox"/>
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.MyApp.CompoundButton.Switch"/>
Compose:
// Checkbox
Checkbox(
checked = checked,
onCheckedChange = { /*...*/ },
colors = CheckboxDefaults.colors(
checkedColor = MaterialTheme.colors.primary,
uncheckedColor = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
checkmarkColor = MaterialTheme.colors.onPrimary
)
)
// Switch
Switch(
checked = checked,
onCheckedChange = { /*...*/ },
colors = SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colors.primary,
checkedTrackColor = MaterialTheme.colors.primary.copy(alpha = 0.5f),
uncheckedThumbColor = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
uncheckedTrackColor = MaterialTheme.colors.onSurface.copy(alpha = 0.3f)
)
)
4. 颜色资源统一管理
在 res/values/colors.xml
和 res/values-night/colors.xml
中定义所有颜色:
<!-- values/colors.xml -->
<color name="primary">#6200EE</color>
<color name="primary_dark">#3700B3</color>
<color name="secondary">#03DAC6</color>
<color name="background">#FAFAFA</color>
<color name="surface">#FFFFFF</color>
<color name="button_background">#6200EE</color>
<color name="switch_thumb">#FFFFFF</color>
<color name="switch_track">#C4C4C4</color>
<!-- values-night/colors.xml -->
<color name="primary">#BB86FC</color>
<color name="primary_dark">#3700B3</color>
<color name="secondary">#03DAC6</color>
<color name="background">#121212</color>
<color name="surface">#1E1E1E</color>
<color name="button_background">#BB86FC</color>
<color name="switch_thumb">#BB86FC</color>
<color name="switch_track">#646464</color>
5. 实用扩展函数
为方便在 Compose 中使用 View 定义的颜色:
@Composable
fun ViewColor(@ColorRes id: Int): Color {
return colorResource(id = id)
}
// 使用示例
Button(
colors = ButtonDefaults.buttonColors(
backgroundColor = ViewColor(R.color.button_background)
)
) {
Text("按钮")
}
通过以上配置,你可以确保项目中 View 和 Compose 的所有组件在浅色和深色模式下保持完全一致的视觉风格。