mcpField
S
styles

styles

github.com/android设计与 UI10.0k2.2k 次安装2 天前更新
代码仓库

关于 styles

面向 Jetpack Compose 自定义组件和主题的 Styles API 迁移技能,涵盖样式定义、状态处理与视觉验证。该技能属于实验性功能,要求使用 alpha 版 Compose API,不支持 Material Design 组件样式。

主要功能

  1. 01分析主题结构与设计令牌
  2. 02建立 ComponentStyles 并接入自定义主题
  3. 03将组件迁移至 Styles API
  4. 04处理交互状态与 Modifier.styleable()
  5. 05通过截图和 UI 测试验证视觉效果

适用场景

  1. 01将自定义组件的样式参数迁移到 Style
  2. 02把设计令牌集中到组件样式定义
  3. 03验证样式迁移是否造成视觉回归

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/android/skills --skill styles

安装后在 Claude Code 中输入/styles确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/android/develop/ui/compose/designsystems/
custom.md
references/android/develop/ui/compose/styles/
fundamentals.md
state-animations.md
styles-vs-modifiers.md
theming.md

详细介绍

限制

  • 告知用户,此技能为 EXPERIMENTAL,需要升级到 Compose 的 alpha 版本,并选择启用 Experimental API。
  • 此技能仅支持自定义 UI 组件和自定义主题。
  • 此技能不支持 Material Design 组件样式。

前提条件

1. 升级依赖项
  • 项目必须使用 compileSdk 版本 37 或更高版本。
  • 项目必须使用 androidx.compose.foundation:foundation 版本 1.12.0-alpha01 或更高版本。
  • 或者,项目必须使用 Compose BOM 版本 2026.04.01 或更高版本。
  • 此 API 要求使用以下确切的软件包:import androidx.compose.foundation.style.Style
2. 配置编译器选项以启用实验性 API

必须在项目级别选择启用实验性 API。将以下代码块添加到模块的 build.gradle.kts 中:

kotlin {
    compilerOptions {
        jvmTarget = JvmTarget.fromTarget("17")
        freeCompilerArgs.add("-opt-in=androidx.compose.foundation.style.ExperimentalFoundationStyleApi")
    }
}

核心工作流和指南

请参阅官方文档以完成具体的开发任务:

分步迁移工作流

第 1 步:分析主题结构
  1. 找到中央主题文件(例如 Theme.kt)。
  2. 找出设计令牌。记录颜色、排版和形状的引用(例如 LocalColorScheme、LocalTypography 或 LocalShapes)。
  3. 如果项目缺少 Jetpack Compose 依赖项,请停止。指示用户先迁移到 Jetpack Compose。
  4. 如果项目导入了 androidx.compose.material.MaterialTheme,请建议先迁移到 Material 3,再继续操作。
第 2 步:建立 ComponentStyles
  1. 在主题目录中创建名为 ComponentStyles.kt 的新文件。

  2. 定义一个顶层数据类来保存组件样式,例如,Jetsnack 中的类名为 JetsnackStyles:

    object ExampleComponentStyles {
        val customButtonStyle: Style = {
    
        }
        val customTextFieldStyle: Style = {
    
        }
    }
    

  3. 通过静态引用在自定义主题中公开此类,此处无需使用 CompositionLocals。

    @Immutable
    class JetsnackTheme(
        // other Design system properties
    ) {
        companion object {
            val colors: CustomThemingWithStyles.JetsnackColors
                @Composable @ReadOnlyComposable
                get() = LocalJetsnackTheme.current.colors
            // ...
    
            // add helper static reference
            val styles: ComponentStyles = ComponentStyles
        }
    }
    

  4. 如果主题令牌通过 CompositionLocals 暴露,请在 StyleScope 上提供扩展,以便直接引用这些令牌。例如:

    val StyleScope.colors: JetsnackColors
        get() = LocalJetsnackTheme.currentValue.colors
    
    val StyleScope.typography: androidx.compose.material3.Typography
        get() = LocalJetsnackTheme.currentValue.typography
    
    val StyleScope.shapes: Shapes
        get() = LocalJetsnackTheme.currentValue.shapes
    

步骤 3:将组件迁移到 Styles API

对于每个自定义组件(例如 CustomButton),请完成以下步骤:

  1. 建立视觉基线(如果有模拟器):
    • 如果无法运行 Android 模拟器: 完全跳过此步骤,继续执行步骤 2。
    • 如果可以运行 Android 模拟器: 按照以下方式截取基线屏幕截图:
      • 选项 A: 查找并运行组件现有的屏幕截图测试。
      • 选项 B(如果没有测试): 使用项目现有的测试框架创建测试,然后运行。
      • 选项 C(如果没有框架): 使用 UI Automator 或 Espresso 创建最简屏幕截图测试,然后运行。
  2. 移除单独的样式参数:从签名中移除 backgroundColor、shape、textStyle 和 contentPadding 等样式参数,即所有 StyleScope 支持的参数。
  3. 添加 style 参数:在函数签名中添加 style: Style = Style。务必确保默认值恰好是 Style(例如 style: Style = Style),而不是 ChipStyleDefault 等特定样式默认值。
  4. 声明状态跟踪:如果组件可交互,请使用交互源创建 MutableStyleState。在 Composable 中更新状态字段(例如 isEnabled),以正确跟踪状态。
  5. 应用可设置样式的修饰符:将根元素上的特定布局修饰符替换为 Modifier.styleable()。
  6. 将默认值移至 ComponentStyles:将组件定义中的硬编码值移至 ComponentStyles.kt 中专用的 Style 实例。
  7. 验证组件: 将开始时截取的基线屏幕截图与新 Composable 渲染的 Compose Preview 进行比较。忽略字符串内容,重点关注布局和样式。反复调整 Compose 代码,直到实现视觉一致。验证完成后,为新的 Composable 编写 Compose UI 测试。
迁移示例

迁移前:

@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    backgroundColor: Color = JetsnackTheme.colors.brandLight,
    disabledBackgroundColor: Color = JetsnackTheme.colors.brandSecondary,
    shape: Shape = JetsnackTheme.shapes.extraLarge,
    textStyle: TextStyle = JetsnackTheme.typography.labelLarge,
    enabled: Boolean = true,
    content: @Composable RowScope.() -> Unit,
) {
    val interactionSource = remember { MutableInteractionSource() }
    Row(
        modifier
            .clickable(onClick = onClick, indication = null, interactionSource = interactionSource)
            .background(if (enabled) backgroundColor else disabledBackgroundColor, shape)
            .defaultMinSize(58.dp, 40.dp),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically,
        content = content,
    )
}

迁移后:

// Exposed via ComponentStyles.kt
object ComponentStyles {
    val buttonStyle = Style {
        background(colors.brandLight)
        shape(shapes.extraLarge)
        minWidth(58.dp)
        minHeight(40.dp)
        textStyle(typography.labelLarge)
        disabled {
            background(colors.brandSecondary)
        }
    }
}

@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    style: Style = Style,
    enabled: Boolean = true,
    content: @Composable RowScope.() -> Unit,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val styleState = rememberUpdatedStyleState(interactionSource) {
        it.isEnabled = enabled
    }
    Row(
        modifier
            .clickable(onClick = onClick, indication = null, interactionSource = interactionSource)
            .styleable(styleState, JetsnackTheme.styles.buttonStyle, style),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically,
        content = content,
    )
}

步骤 4:验证更改
  1. 构建项目。确认没有编译错误。
  2. 运行模块的屏幕截图测试。
  3. 比较更新前后整个应用的视觉输出。确认没有出现视觉布局回归。

评论

支持 Markdown