在 Compose 中将 XML 主题迁移到 Material 3

在现有应用中引入 Compose 时,您需要迁移 Material XML 主题,以便对 Compose 组件使用 MaterialTheme。这意味着应用的主题将会有 2 个可信来源:基于 View 的主题以及 Compose 主题。样式上的任何更改都需要在多处实施。应用完全迁移到 Compose 后,请移除 XML 主题。

您可以使用 Material Theme Builder 工具迁移颜色。

当您开始从 XML 迁移到 Compose 时,请将主题迁移到 Material 3 Compose 主题。

术语库

术语 定义
MaterialTheme 可组合函数,用于为 Compose 界面组件提供主题(颜色、排版、形状)。
Shapes Compose 对象,用于为 MaterialTheme 定义自定义组件形状。
Typography Compose 对象,用于为 MaterialTheme 定义自定义文本样式(字体系列、大小、粗细)。
ColorScheme Compose 对象,用于为 MaterialTheme 定义自定义配色方案。
XML 主题 在 XML 文件中定义的 Android 主题系统,由 View 系统使用。

限制

在迁移之前,请注意以下限制:

  • 本指南仅重点介绍如何迁移到 Material 3。如需了解如何从 替代设计系统进行迁移,请参阅 Material 2Compose 中的自定义设计系统
  • 最终目标是完全迁移到 Compose,这样便可以移除 XML 主题。本指南介绍了如何迁移,但未介绍如何最终移除 XML 主题。

第 1 步:评估设计系统

确定 XML View 项目中使用的设计系统。 分析迁移路径和必要步骤,以便在 Compose 中将现有设计系统迁移到 Material 3。

第 2 步:确定主题源文件

在 XML 中,您需要编写 ?attr/colorPrimary。在 Compose 中,您可以使用 MaterialTheme.* 访问主题值:

确定并找到主题所需的所有 XML 资源和文件:浅色和深色配色方案和限定符、主题、形状、尺寸、排版、样式和其他相关文件。

字符串等资源可以按原样重复使用,无需迁移。

第 3 步:迁移颜色

关键原则: XML 使用命名十六进制颜色。 Material 3 使用 语义角色(例如,primaryonPrimarysurface)。停止按十六进制命名颜色;按角色命名颜色。

示例:

XML 颜色名称 Material 3 角色
colorPrimary primary
colorPrimaryDark / colorPrimaryVariant primaryContainersecondary
colorAccent secondarytertiary
colorOnPrimary onPrimary
android:colorBackground background
colorSurface surface
colorOnSurface onSurface
colorError error
colorOnError onError
colorOutline outline
colorSurfaceVariant surfaceVariant
colorOnSurfaceVariant onSurfaceVariant

将深色和浅色配色方案从 XML 迁移到 Material 3 Compose 中的等效配色方案。

第 4 步:迁移自定义形状和排版

  • 如果您的应用使用自定义形状:

    1. 在 Compose 代码中,定义 Shapes 对象以复制 XML 形状定义。
    2. 将此 Shapes 对象提供给 MaterialTheme

      如需了解详情,请参阅形状

  • 如果您的应用使用自定义排版:

    1. 在 Compose 代码中,定义 Typography 对象以复制 XML 文本样式和字体定义。
    2. 将此 Typography 对象提供给 MaterialTheme

      如需了解详情,请参阅排版

Compose 角色 XML 名称
displayLarge TextAppearance.Material3.DisplayLarge
displayMedium TextAppearance.Material3.DisplayMedium
displaySmall TextAppearance.Material3.DisplaySmall
headlineLarge TextAppearance.Material3.HeadlineLarge
headlineMedium TextAppearance.Material3.HeadlineMedium
headlineSmall TextAppearance.Material3.HeadlineSmall
titleLarge TextAppearance.Material3.TitleLarge
titleMedium TextAppearance.Material3.TitleMedium
titleSmall TextAppearance.Material3.TitleSmall
bodyLarge TextAppearance.Material3.BodyLarge
bodyMedium TextAppearance.Material3.BodyMedium
bodySmall TextAppearance.Material3.BodySmall
labelLarge TextAppearance.Material3.LabelLarge
labelMedium TextAppearance.Material3.LabelMedium
labelSmall TextAppearance.Material3.LabelSmall

第 5 步:迁移样式 (styles.xml)

XML 样式 (styles.xml) 系统定义了以下内容的样式和外观:

  1. 窗口和对话框的微件、组件、主题
  2. 排版
  3. 主题和叠加层
  4. 形状

XML View 和组件组合了多个属性来创建样式。 它们通过以下两种不同的方式从 styles.xml 设置样式:

  1. 在 XML View 中直接且明确地设置“style="@style/..."
  2. 将样式间接且隐式地设置为较大主题 (theme.xml) 的一部分

样式在 Compose 中没有 直接 等效项,而是作为以下内容传递给可组合项: 参数或修饰符,使用 AppTheme 中定义的新实验性 Styles API,或者通过创建 具有已定义样式的分层可重复使用的可组合项变体。

提供单独的 @Composable 函数,这些函数根据样式和基本组件命名,以表示这些组件的样式和用例的差异。

  • 模式: 如果 XML 元素使用自定义样式 (例如,style="@style/MyPrimaryButton"), 请勿尝试内联复制该样式。而是建议创建特定的可组合项。
  • 示例
    • XML<Button style="@style/MyPrimaryButton" ... />
    • ComposeMyPrimaryButton(onClick = { ... })
  • 常见属性组: 如果样式设置了常见修饰符(例如,内边距 + 高度),请将其提取到可读的扩展属性或共享的 Modifier 变量中。

常见示例

XML Compose
Theme.Material3.* MaterialTheme(colorScheme, typography, shapes) { }
TextAppearance.Material3.BodyMedium TextStyle(...)Typography(bodyMedium = ...) 中定义
ShapeAppearance.*.SmallComponent Shapes(small = RoundedCornerShape(X.dp))
Widget.Material3.Button Button(colors = ButtonDefaults.buttonColors(...))
Widget.Material3.CardView Card(shape=..., elevation=..., colors=...)
Widget.*.TextInputLayout.OutlinedBox OutlinedTextField(colors = OutlinedTextFieldDefaults.colors(...))
Widget.*.Chip.Filter FilterChip(colors = FilterChipDefaults.filterChipColors(...))
Widget.*.Toolbar.Primary TopAppBar(colors = TopAppBarDefaults.topAppBarColors(...))
Widget.*.FloatingActionButton FloatingActionButton(containerColor = ...)
backgroundTint containerColorComponentDefaults.ComponentColors()
android:textColor contentColorComponentDefaults.ComponentColors()
cornerRadius shape = RoundedCornerShape(X.dp)
android:elevation elevation = ComponentDefaults.elevation(defaultElevation = X.dp)
android:padding contentPadding = PaddingValues(...)Modifier.padding()
android:minHeight Modifier.heightIn(min = X.dp)
strokeColor + strokeWidth border = BorderStroke(width, color)
android:textSize fontSize = X.spTextStyle

第 6 步:验证主题迁移

始终使用原始 XML 主题中的现有主题值作为 Compose 中新 Material 主题的可信来源。 切勿在迁移期间创建新的主题值,以保持品牌一致性并避免视觉回归。

验证所有新的 Compose 主题值是否与现有 XML 值匹配。 请勿对任何迁移的值进行硬编码。