关于 jetpack-compose-m3
使用 Wear Compose Material3 构建和迁移 Wear OS 界面的技能,指导开发者检查版本与组件示例,并遵循适用于 Wear 的布局和导航规范。
主要功能
- 01Wear Compose Material3 迁移指南
- 02版本与兼容性检查
- 03组件示例核验
- 04Scaffold 与列表布局指南
- 05Material 主题与默认值
- 06Wear 导航规范
适用场景
- 01迁移旧版 Wear Compose 页面
- 02添加 Wear Material3 组件
- 03构建可滚动的 Wear OS 页面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ android/ skills --skill jetpack-compose-m3安装后在 Claude Code 中输入/jetpack-compose-m3确认已加载。
附带文件references/android/training/wearables/compose/migrate-to-material3.md
详细介绍
前提条件与兼容性
- Wear OS Compose Material3 版本: 如果有可用的内部工具来确定
androidx.wear.compose:compose-material3的最新稳定版本{VERSION},请使用该工具。- 否则,获取官方 Maven 元数据 XML,以确定
{VERSION}(最高版本号,忽略-alpha、-beta或-rc)。
- 否则,获取官方 Maven 元数据 XML,以确定
- 严格遵循: 如果某个版本被列为稳定版,就必须使用该版本,除非用户另有要求。不要因为编辑器中出现初始的“Unresolved reference”错误或过时的网页搜索结果而降级。
- Kotlin 版本: 使用 Wear Compose Material3 时,Kotlin 版本必须为 2.0.0 或 更高版本。
- Compose 编译器:
- 如果 Kotlin 版本为 2.0.0+,项目必须使用
org.jetbrains.kotlin.plugin.composeGradle 插件。 - 如果 Kotlin 版本为 < 2.0.0,项目必须在
composeOptions中使用kotlinCompilerExtensionVersion,并与 Compose 与 Kotlin 兼容性映射表匹配。
- 如果 Kotlin 版本为 2.0.0+,项目必须使用
- 最低 SDK: 确保
minSdk至少为 25(Wear OS 2.0)。 - 样例提取要求:Wear Compose 库附带额外的 JAR 文件,其中包含每个组件各自的样例。在 Capability 2 中的样例提取到本地缓存之前,不得提出代码更改。库源文件并不完整,不能替代这些样例;跳过提取属于环境设置失败。
注意事项
- 必须同步并验证: 在
libs.versions.toml或build.gradle.kts中更新版本后,必须先执行 Gradle 同步,再重构任何代码。这样可以确保环境已正确解析这些库。 - 禁止猜测(错误处理流程): 如果成功同步后遇到“Unresolved Reference”或 API 不匹配,不要尝试通过降低库版本来“修复”。
能力与工具
Capability 1:迁移
从较旧版本的 Wear OS Compose 或 Horologist 迁移时,请遵循以下指导。
- 除非开发者另有说明,否则使用
{VERSION}中最新的稳定版 Wear Compose Material3。 - 阅读迁移指南。
- 使用迁移指南中的官方组件映射。
- 重构任何组件(例如,将
Chip->Button)之前,请检查参数名称、插槽类型和“Expressive”设计标记。 - 不要使用 Horologist Composables、Compose Layout 或 Compose Material 库。
- 始终对照 Capability 3 中的组件指导进行检查。
- 迁移后,截图测试可能会失败:即使迁移到非常相似的组件,预期的默认内边距和位置也会发生变化。不要试图刻意匹配迁移前的截图,应优先采用 Material3 默认值。
Capability 2:组件样例
Wear Compose 为每个组件都提供了独立样例,位于
<artifact>-<version>-samples-sources.jar 文件中。使用 compose-material3、compose-foundation 或 compose-navigation3 时,Gradle 会自动下载这些 JAR 文件以及主库 JAR。
添加或调整 Wear Compose Material3 组件时,请使用规范的组件样例。
严格遵循:提取样例并非可选步骤。在提取并阅读样例之前,禁止实现任何代码。使用其他搜索工具替代此步骤,或假定库文档已足够,均违反协议。继续之前,必须通过读取一个样例文件来验证本地缓存。
步骤 1:准备
- 检查
build.gradle.kts或libs.versions.toml,确保 Wear Compose 版本与{VERSION}匹配。 - 执行 Gradle 同步,确保已下载必要的依赖项。
步骤 2:检查本地缓存
- 定义缓存目录路径:
<SKILL_ROOT>/samples/{VERSION}/。不要选择其他位置。 - 检查此目录是否存在,并且是否包含带有
.kt文件的子目录。- 如果是(缓存命中): 继续执行步骤 4。
- 如果否(缓存未命中): 继续执行步骤 3。
步骤 3:检查 Gradle 缓存
-
示例源文件存储在 Gradle 缓存中。为避免缓慢的暴力搜索:
- 确定 Gradle 用户主目录(通常为
~/.gradle,也可检查$GRADLE_USER_HOME)。 - 缓存根目录为
<GRADLE_USER_HOME>/caches。将其称为<CACHE_ROOT>。
- 确定 Gradle 用户主目录(通常为
-
将列表
["material3", "foundation"]中的项目定义为{ARTIFACT}。如果使用androidx.wear.compose.navigation3库,也将 "navigation3" 加入列表。 -
对列表中的每个
{ARTIFACT}:-
构造库的预期相对路径段:
androidx.wear.compose/compose-{ARTIFACT}/{VERSION}。 -
运行有针对性的
find命令。以下示例经过了效率优化:find <CACHE_ROOT>/modules-2/files-2.1/androidx.wear.compose/compose-{ARTIFACT}/{VERSION}/ \ -name "*samples-sources.jar"
-
-
将此 JAR 用作官方示例源文件。
-
使用
unzip -j将每个 JAR 的内容解压到<SKILL_ROOT>/samples/{VERSION}/{ARTIFACT}/,以扁平化目录结构。 -
直接继续执行步骤 4。
步骤 4:阅读示例并实现
- 阅读相关的
.kt示例文件。 - 将这些与版本匹配的官方示例作为以下内容的准确信息来源:
- 必需参数和插槽名称。
- 默认样式和字体排版令牌。
- 交互行为(例如:
onClick、onLongClick)。 - 组件嵌套关系(例如:
AppScaffold->ScreenScaffold)。
能力 3:组件使用指导
强制要求:将此能力用作检查清单,核对任何组件的使用情况。它提供了关于如何在实践中使用各个组件的更全面指导,而不仅仅是组件语法。
-
AppScaffold和ScreenScaffold- [ ] 将
AppScaffold用作外层容器,并以ScreenScaffold作为其子项。 - [ ] 只使用一个
AppScaffold,并可使用任意数量的ScreenScaffold。
- [ ] 将
-
ScalingLazyColumn- 改用TransformingLazyColumn。 -
TransformingLazyColumn- 你需要导入以下内容:import androidx.wear.compose.foundation.lazy.TransformingLazyColumn import androidx.wear.compose.foundation.lazy.TransformingLazyColumnDefaults import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState // ... import androidx.wear.compose.material3.lazy.rememberTransformationSpec import androidx.wear.compose.material3.lazy.transformedHeight
规范示例:
val columnState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold( scrollState = columnState ) { contentPadding -> TransformingLazyColumn( state = columnState, contentPadding = contentPadding ) { item { ListHeader( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) { Text(text = "Header") } } // ... other items item { Button( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding), transformation = SurfaceTransformation(transformationSpec), onClick = { /* ... */ }, icon = { Icon( imageVector = Icons.Default.Build, contentDescription = "build", ) }, ) { Text( text = "Build", maxLines = 1, overflow = TextOverflow.Ellipsis, ) } } } }
- [ ] 使用
TransformingLazyColumn,而不是ScalingLazyColumn。 - [ ] 必须将
ScreenScaffold的contentPadding参数传递给TransformingLazyColumn。 - [ ] 使用
minimumVerticalContentPadding修饰符设置所需的顶部和底部内边距。- 该值应取自默认值,例如
ButtonDefaults、CardDefaults、`ListHeaderDefaults。 - 注意:这是一个作用域修饰符,可在
TransformingLazyColumnItemScope中使用。
- 该值应取自默认值,例如
- [ ] 确保列表会变形和缩放。
- [ ] 使用
transformedHeight修饰符。 - [ ] 使用
transform = SurfaceTransform(...)。 - [ ] 如果要配置可吸附的列表,请同时使用
flingBehavior和rotaryScrollableBehavior:
val columnState = rememberTransformingLazyColumnState() ScreenScaffold(scrollState = columnState) { contentPadding -> TransformingLazyColumn( state = columnState, flingBehavior = TransformingLazyColumnDefaults.snapFlingBehavior(columnState), rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior(columnState) ) { // ... // ... } }
- [ ] 使用
-
ScreenScaffold- [ ] 使用
!LocalScrollCaptureInProgress.current对scrollIndicator进行保护。
- [ ] 使用
-
EdgeButton- [ ] 不要将其用作
TransformingLazyColumn中的最后一项。请改用ScreenScaffold中的插槽。 - [ ] 在
TransformingLazyColumn中使用时,添加所需的过滚行为:
val columnState = rememberTransformingLazyColumnState() ScreenScaffold( scrollState = columnState, edgeButton = { EdgeButton( onClick = { /* TODO */ }, modifier = Modifier.scrollable( columnState, orientation = Orientation.Vertical, reverseDirection = true, // Apply overscroll to the EdgeButton for proper scrolling behavior. overscrollEffect = rememberOverscrollEffect(), ) ) { Text("More") } } ) { contentPadding -> TransformingLazyColumn( contentPadding = contentPadding, state = columnState, ) { // ... // ... } }
- [ ] 不要将其用作
-
Column- [ ] 如果屏幕永远不会滚动(即使使用最大的系统字体),则将其用作
ScreenScaffold的直接子项。 - [ ] 其他情况下均改用
TransformingLazyColumn。
- [ ] 如果屏幕永远不会滚动(即使使用最大的系统字体),则将其用作
-
样式
- [ ] 不要将文本大小硬编码,请使用
MaterialTheme中的typography。 - [ ] 不要将颜色硬编码,请使用
MaterialTheme中的colorScheme。
- [ ] 不要将文本大小硬编码,请使用
-
使用组件默认值:
- [ ]
Button等组件都有对应的ButtonDefaults对象。 - 使用内边距和样式值时,请检查并使用任何组件对应的
*Defaults对象,优先于硬编码值。
- [ ]
-
使用 Wear 专用预览:
- [ ]
WearPreviewDevices - [ ]
WearPreviewFontScales
- [ ]
-
环境模式
- [ ] 使用
LocalAmbientModeManager,而不是AmbientLifecycleObserver。
- [ ] 使用
-
导航
- [ ] 全新添加导航时,请使用 Navigation3。
- [ ] 在 Wear OS 中使用 Navigation3 时,请使用 Wear Compose
compose-navigation3库中的SwipeDismissableSceneStrategy()。