在 Compose for Wear OS 中从 Material 2.5 迁移至 Material 3

Material 3 Expressive 是新一代 Material Design。它包括更新后的主题、组件和动态配色等个性化功能。

本指南重点介绍如何将应用从 Wear Compose Material 2.5 (androidx.wear.compose) Jetpack 库迁移至 Wear Compose Material 3 (androidx.wear.compose.material3) Jetpack 库。

方法

如需将应用代码从 M2.5 迁移到 M3,请按照 Compose Material 迁移手机指南中所述的相同方法操作,尤其是:

依赖项

M3 有单独的 M2.5 软件包和版本:

M2.5

implementation("androidx.wear.compose:compose-material:1.4.0")

M3

implementation("androidx.wear.compose:compose-material3:1.7.0")

请参阅 Wear Compose 版本页面,了解最新的 M3 版本。

Wear Compose Foundation 库版本 1.7.0 引入了一些旨在与 Material 3 组件搭配使用的新组件。同样,在 Wear OS 6(API 级别 36)或更高版本上运行时,Wear Compose Navigation 库中的 SwipeDismissableNavHost 具有更新的动画。更新到 Wear Compose Material 3 版本时,我们建议您同时更新 Wear Compose Foundation 和 Navigation 库:

implementation("androidx.wear.compose:compose-foundation:1.7.0")
implementation("androidx.wear.compose:compose-navigation:1.7.0")

主题

在 M2.5 和 M3 中,主题可组合项的名称都是 MaterialTheme,但导入软件包和参数有所不同。在 M3 中,Colors 参数已重命名为 ColorScheme,并引入了 MotionScheme 来实现过渡。

M2.5

import androidx.wear.compose.material.MaterialTheme

MaterialTheme(
    colors = AppColors,
    typography = AppTypography,
    shapes = AppShapes,
    content = content
)

M3

import androidx.wear.compose.material3.MaterialTheme
// ...
    MaterialTheme(
        colorScheme = ColorScheme(),
        typography = Typography(),
        shapes = Shapes(),
        motionScheme = MotionScheme.standard(),
        content = { /*content here*/ }
    )

颜色

M3 中的颜色系统与 M2.5 有显著的差异。颜色参数的数量有所增加,使用不同的名称,而且与 M3 组件的映射方式也不同。在 Compose 中,这适用于 M2.5 Colors 类、M3 ColorScheme 类和相关函数:

M2.5

import androidx.wear.compose.material.Colors

val appColorScheme: Colors = Colors(
   // M2.5 Color parameters
)

M3

import androidx.wear.compose.material3.ColorScheme
// ...
    val appColorScheme: ColorScheme = ColorScheme(
        // M3 ColorScheme parameters
    )

下表介绍了 M2.5 和 M3 之间的主要区别:

M2.5 M3
Colors 已重命名为 ColorScheme
13 种颜色 29 种颜色
不适用 新的动态配色主题
不适用 新增了三级颜色,让您更具表现力

动态配色主题

动态配色主题是 M3 中的一项新功能。如果用户更改表盘颜色,界面中的颜色也会随之更改。

使用 dynamicColorScheme 函数实现动态配色方案,并在动态配色方案不可用的情况下提供 defaultColorScheme 作为后备方案。

@Composable
fun myApp() {
    val dynamicColorScheme = dynamicColorScheme(LocalContext.current)
    MaterialTheme(colorScheme = dynamicColorScheme ?: myBrandColors) {}
}

internal val myBrandColors: ColorScheme = ColorScheme( /* Specify colors here */)

排版

M3 中的排版系统与 M2.5 不同,它包含以下特性:

  • 九种全新文字样式
  • 灵活字体,可用于自定义不同粗细、宽度和圆度的字体比例
  • AnimatedText,它使用灵活字体

M2.5

import androidx.wear.compose.material.Typography

val Typography = Typography(
   // M2.5 TextStyle parameters
)

M3

import androidx.wear.compose.material3.Typography

val Typography = Typography(
    // M3 TextStyle parameters
)

Flex 字体

借助灵活字体,设计人员可以针对特定尺寸指定类型宽度和粗细。

文本样式

M3 中提供以下 TextStyle。各种 M3 组件默认使用这些指标。

排版 TextStyle
显示 displayLarge、displayMedium、displaySmall
标题 titleLarge、titleMedium、titleSmall
标签 labelLarge、labelMedium、labelSmall
正文 bodyLarge、bodyMedium、bodySmall、bodyExtraSmall
数词 numeralExtraLarge、numeralLarge、numeralMedium、numeralSmall、numeralExtraSmall
弧形 arcLarge、arcMedium、arcSmall

形状

M3 采用与 M2.5 不同的形状系统。形状参数数量有所增加,其命名方式有所不同,而且它们与 M3 组件的映射方式也不同。以下形状大小可供选择:

  • 超小号
  • 小
  • 中号
  • 大
  • 超大

在 Compose 中,这适用于 M2.5 Shapes 类和 M3 Shapes 类:

M2.5

import androidx.wear.compose.material.Shapes

val Shapes = Shapes(
   // M2.5 Shapes parameters
)

M3

import androidx.wear.compose.material3.Shapes

val Shapes = Shapes(
    // M3 Shapes parameters
)

从在 Compose 中从 Material 2 迁移至 Material 3 中提供的“形状”参数映射着手。

形状变形

M3 引入了形状变形功能:形状现在会根据互动而变形。

形状变形行为可作为多种圆形按钮的变体使用,请参阅以下支持形状变形的按钮列表:

按钮 形状变形函数
IconButton IconButtonDefaults.animatedShapes 会在按下时为图标按钮添加动画效果
IconToggleButton IconToggleButtonDefaults.animatedShapes 会在按下时为图标切换按钮添加动画效果,而
IconToggleButtonDefaults.variantAnimatedShapes 会在按下和选中/取消选中时为图标切换按钮添加动画效果
TextButton TextButtonDefaults.animatedShapes 会在按下时为文字按钮添加动画效果
TextToggleButton TextToggleButtonDefaults.animatedShapes 会在按下时为文本切换按钮添加动画效果,而 TextToggleButtonDefaults.variantAnimatedShapes 会在按下和选中/取消选中时为文本切换按钮添加动画效果

组件和布局

M2.5 中的大多数组件和布局在 M3 中均具有可用性。不过,某些 M3 组件和布局在 M2.5 中不存在。此外,M3 中的一些组件的变体也比 M2.5 中的变体更多。

尽管某些组件需要特别加以注意,但建议从以下函数映射入手:

Material 2.5 Material 3
androidx.wear.compose.material.dialog.Alert androidx.wear.compose.material3.AlertDialog
androidx.wear.compose.material.Button androidx.wear.compose.material3.IconButton 或 androidx.wear.compose.material3.TextButton
androidx.wear.compose.material.Card androidx.wear.compose.material3.Card
androidx.wear.compose.material.TitleCard androidx.wear.compose.material3.TitleCard
androidx.wear.compose.material.AppCard androidx.wear.compose.material3.AppCard
androidx.wear.compose.material.Checkbox 无 M3 等效项,请迁移到 androidx.wear.compose.material3.CheckboxButton 或 androidx.wear.compose.material3.SplitCheckboxButton
androidx.wear.compose.material.Chip androidx.wear.compose.material3.Button 或
androidx.wear.compose.material3.OutlinedButton 或
androidx.wear.compose.material3.FilledTonalButton 或
androidx.wear.compose.material3.ChildButton
androidx.wear.compose.material.CompactChip androidx.wear.compose.material3.CompactButton
androidx.wear.compose.material.InlineSlider androidx.wear.compose.material3.Slider
androidx.wear.compose.material.LocalContentAlpha() 已被移除,因为 Material 3 中的 Text 或 Icon 不再使用它
androidx.wear.compose.material.PositionIndicator androidx.wear.compose.material3.ScrollIndicator
androidx.wear.compose.material.RadioButton 无 M3 等效项,请迁移到 androidx.wear.compose.material3.RadioButton 或 androidx.wear.compose.material3.SplitRadioButton
androidx.wear.compose.material.SwipeToRevealCard androidx.wear.compose.material3.SwipeToReveal
androidx.wear.compose.material.SwipeToRevealChip androidx.wear.compose.material3.SwipeToReveal
androidx.wear.compose.material.Scaffold androidx.wear.compose.material3.AppScaffold 和 androidx.wear.compose.material3.ScreenScaffold
androidx.wear.compose.material.SplitToggleChip 没有 M3 等效项,请迁移到 androidx.wear.compose.material3.SplitCheckboxButton、androidx.wear.compose.material3.SplitSwitchButton 或 androidx.wear.compose.material3.SplitRadioButton
androidx.wear.compose.material.Switch 没有 M3 等效项,请迁移到 androidx.wear.compose.material3.SwitchButton 或 androidx.wear.compose.material3.SplitSwitchButton
androidx.wear.compose.material.ToggleButton androidx.wear.compose.material3.IconToggleButton 或 androidx.wear.compose.material3.TextToggleButton
androidx.wear.compose.material.ToggleChip androidx.wear.compose.material3.CheckboxButton 或
androidx.wear.compose.material3.RadioButton 或
androidx.wear.compose.material3.SwitchButton
androidx.wear.compose.material.Vignette 已移除,因为 Wear OS 的 Material 3 Expressive 设计中未包含此元素

下面列出了一些常见的 Material 3 组件及其 Material 2.5 等效项:

Material 3 Material 2.5 等效组件(如果不是 M3 中的新组件)
androidx.wear.compose.material3.AlertDialog androidx.wear.compose.material.dialog.Alert
androidx.wear.compose.material3.AnimatedPage 新
androidx.wear.compose.material3.AnimatedText 新
androidx.wear.compose.material3.AppScaffold androidx.wear.compose.material.Scaffold(使用 androidx.wear.compose.material3.ScreenScaffold)
androidx.wear.compose.material3.Button androidx.wear.compose.material.Chip
androidx.wear.compose.material3.ButtonGroup 新
androidx.wear.compose.material3.Card androidx.wear.compose.material.Card
androidx.wear.compose.material3.CheckboxButton 带有复选框切换控件的 androidx.wear.compose.material.ToggleChip
androidx.wear.compose.material3.ChildButton androidx.wear.compose.material.Chip(仅当不需要背景时)
androidx.wear.compose.material3.CircularProgressIndicator androidx.wear.compose.material.CircularProgressIndicator
androidx.wear.compose.material3.CompactButton androidx.wear.compose.material.CompactChip
androidx.wear.compose.material3.ConfirmationDialog androidx.wear.compose.material.dialog.Confirmation
androidx.wear.compose.material3.curvedText androidx.wear.compose.material.curvedText
androidx.wear.compose.material3.DatePicker 新
androidx.wear.compose.material3.Dialog androidx.wear.compose.material.dialog.Dialog
androidx.wear.compose.material3.EdgeButton 新
androidx.wear.compose.material3.FadingExpandingLabel 新
androidx.wear.compose.material3.FilledTonalButton 需要色调按钮背景时使用 androidx.wear.compose.material.Chip
androidx.wear.compose.material3.HorizontalPageIndicator androidx.wear.compose.material.HorizontalPageIndicator
androidx.wear.compose.material3.HorizontalPagerScaffold 新
androidx.wear.compose.material3.Icon androidx.wear.compose.material.Icon
androidx.wear.compose.material3.IconButton androidx.wear.compose.material.Button
androidx.wear.compose.material3.IconToggleButton androidx.wear.compose.material.ToggleButton
androidx.wear.compose.material3.LevelIndicator 新
androidx.wear.compose.material3.LinearProgressIndicator 新
androidx.wear.compose.material3.ListHeader androidx.wear.compose.material.ListHeader
androidx.wear.compose.material3.ListSubHeader 新
androidx.wear.compose.material3.MaterialTheme androidx.wear.compose.material.MaterialTheme
androidx.wear.compose.material3.OpenOnPhoneDialog 新
androidx.wear.compose.material3.Picker androidx.wear.compose.material.Picker
androidx.wear.compose.material3.PickerGroup androidx.wear.compose.material.PickerGroup
androidx.wear.compose.material3.RadioButton 带有单选按钮切换控件的 androidx.wear.compose.material.ToggleChip
androidx.wear.compose.material3.ScreenScaffold androidx.wear.compose.material.Scaffold(使用 androidx.wear.compose.material3.AppScaffold)
androidx.wear.compose.material3.ScrollIndicator androidx.wear.compose.material.PositionIndicator
androidx.wear.compose.material3.scrollAway androidx.wear.compose.material.scrollAway
androidx.wear.compose.material3.SegmentedCircularProgressIndicator 新
androidx.wear.compose.material3.Slider androidx.wear.compose.material.InlineSlider
androidx.wear.compose.material3.SplitRadioButton androidx.wear.compose.material.SplitToggleChip
androidx.wear.compose.material3.SplitCheckboxButton androidx.wear.compose.material.SplitToggleChip
androidx.wear.compose.material3.SplitSwitchButton androidx.wear.compose.material.SplitToggleChip
androidx.wear.compose.material3.Stepper androidx.wear.compose.material.Stepper
androidx.wear.compose.material3.SwipeToDismissBox androidx.wear.compose.material.SwipeToDismissBox
androidx.wear.compose.material3.SwipeToReveal androidx.wear.compose.material.SwipeToRevealCard 和 androidx.wear.compose.material.SwipeToRevealChip
androidx.wear.compose.material3.SwitchButton 带有开关切换控件的 androidx.wear.compose.material.ToggleChip
androidx.wear.compose.material3.Text androidx.wear.compose.material.Text
androidx.wear.compose.material3.TextButton androidx.wear.compose.material.Button
androidx.wear.compose.material3.TextToggleButton androidx.wear.compose.material.ToggleButton
androidx.wear.compose.material3.TimeText androidx.wear.compose.material.TimeText
androidx.wear.compose.material3.VerticalPagerScaffold 新

最后,以下是 Wear Compose Foundation 库中的一些相关组件的列表:

Wear Compose Foundation 1.7.0
androidx.wear.compose.foundation.hierarchicalFocusGroup 用于注释应用中的可组合项,以跟踪组合的活跃部分并协调焦点。
androidx.wear.compose.foundation.pager.HorizontalPager 一个基于 Compose Foundation 组件构建的水平滚动分页器,具有 Wear 专用增强功能,可提升性能并遵循 Wear OS 指南。
androidx.wear.compose.foundation.pager.VerticalPager 一个基于 Compose Foundation 组件构建的垂直滚动分页器,具有 Wear 专属增强功能,可提高性能并遵守 Wear OS 指南。
androidx.wear.compose.foundation.lazy.TransformingLazyColumn 可用于代替 ScalingLazyColumn 为每个项添加滚动转换效果。

按钮

M3 中的按钮与 M2.5 中的按钮不同。M2.5 芯片已替换为按钮。Button 实现为 Text、maxLines 和 textAlign 提供默认值。这些默认值可在 Text 元素中替换。

M2.5

import androidx.wear.compose.material.Chip

//M2.5 Buttons
Chip(...)
CompactChip(...)
Button(...)

M3

//M3 Buttons
Button(onClick = { }){}
CompactButton(onClick = { }){}
IconButton(onClick = { }){}
TextButton(onClick = { }){}

M3 还包含新的按钮变体。请查看 Compose Material 3 API 参考文档概览。

M3 引入了一个新按钮:EdgeButton。EdgeButton 有 4 种不同的尺寸:超小号、小号、中号和大号。EdgeButton 实现根据大小为 maxLines 提供默认值,该值可以自定义。

如果您使用的是 TransformingLazyColumn 或 ScalingLazyColumn,请将 EdgeButton 传递到 ScreenScaffold 中,以便它在滚动时改变形状,而不是添加 EdgeButton 作为最终的列表项。请参阅以下代码,了解如何将 EdgeButton 与 ScreenScaffold 和 TransformingLazyColumn 搭配使用。

val state = rememberTransformingLazyColumnState()
ScreenScaffold(
    scrollState = state,
    edgeButton = {
        EdgeButton(
            onClick = { },
            modifier = Modifier.scrollable(
                state,
                orientation = Orientation.Vertical,
                reverseDirection = true,
                // Apply overscroll to the EdgeButton for proper scrolling behavior.
                overscrollEffect = rememberOverscrollEffect(),
            ),
        ) {
            Text(stringResource(R.string.show))
        }
    },
) { contentPadding ->
    TransformingLazyColumn(state = state, contentPadding = contentPadding) {
        // additional code here
        // ...
    }
}

Scaffold

M3 采用与 M2.5 不同的 Scaffold。在 M3 中,AppScaffold 和新的 ScreenScaffold 可组合项已取代 Scaffold。AppScaffold 和 ScreenScaffold 会布局屏幕的结构,并协调 ScrollIndicator 和 TimeText 组件的转换。

AppScaffold 可让静态界面元素(例如 TimeText)在应用内转换期间(例如滑动关闭)保持可见。它为主要应用内容提供了一个 slot,该内容通常由导航组件(例如 SwipeDismissableNavHost)提供

您为 Activity 声明一个 AppScaffold,并为每个 Screen 使用一个 ScreenScaffold。 AppScaffold 会向屏幕添加默认的 TimeText 组件。如果您想自定义此设置,可以使用 timeText 参数替换它。

M2.5

import androidx.wear.compose.material.Scaffold

Scaffold {...}

M3

    AppScaffold {
        val navController = rememberSwipeDismissableNavController()
        SwipeDismissableNavHost(
            navController = navController,
            startDestination = "message_list"
        ) {
            composable("message_list") {
                MessageList(onMessageClick = { id ->
                    navController.navigate("message_detail/$id")
                })
            }
            composable("message_detail/{id}") {
                MessageDetail(id = it.arguments?.getString("id")!!)
            }
        }
    }
}

// Implementation of one of the screens in the navigation
@Composable
fun MessageDetail(id: String) {
    // .. Screen level content goes here
    val scrollState = rememberTransformingLazyColumnState()
    val transformationSpec = rememberTransformationSpec()

    ScreenScaffold(
        scrollState = scrollState,
    ) { contentPadding ->
        // Screen content goes here
        // ...

如果您将 HorizontalPager 与 HorizontalPageIndicator 搭配使用,则可以迁移到 HorizontalPagerScaffold。HorizontalPagerScaffold 放置在 AppScaffold 内。AppScaffold 和 HorizontalPagerScaffold 会布局 Pager 的结构,并协调 HorizontalPageIndicator 和 TimeText 组件的转换。

HorizontalPagerScaffold 默认会在屏幕中心位置显示 HorizontalPageIndicator,并根据 Pager 是否正在分页来协调显示和隐藏 TimeText 和 HorizontalPageIndicator,这由 PagerState 决定。

此外,还有一个新的 AnimatedPage 组件,该组件可根据页面在 Pager 中的位置,以缩放和幕帘效果为 Pager 中的页面添加动画效果。

AppScaffold {
    val pagerState = rememberPagerState(pageCount = { 10 })

    HorizontalPagerScaffold(pagerState = pagerState) {
        HorizontalPager(
            state = pagerState,
            flingBehavior =
                PagerScaffoldDefaults.snapWithSpringFlingBehavior(
                    state = pagerState
                ),
        ) { page ->
            AnimatedPage(pageIndex = page, pagerState = pagerState) {
                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 = "Pager sample")
                            }
                        }
                        item {
                            Card(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .transformedHeight(this, transformationSpec)
                                    .minimumVerticalContentPadding(
                                        CardDefaults.minimumVerticalListContentPadding
                                    ),
                                transformation = SurfaceTransformation(transformationSpec),
                            ) {
                                if (page == 0) {
                                    Text(text = "Page #$page. Swipe right")
                                } else {
                                    Text(text = "Page #$page. Swipe left and right")
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

最后,M3 引入了 VerticalPagerScaffold,它遵循与 HorizontalPagerScaffold 相同的模式:

@Composable
fun VerticalPagerScaffoldSample() {
    AppScaffold {
        val pagerState = rememberPagerState(pageCount = { 10 })

        VerticalPagerScaffold(pagerState = pagerState) {
            VerticalPager(
                state = pagerState,
                flingBehavior =
                    PagerScaffoldDefaults.snapWithSpringFlingBehavior(
                        state = pagerState
                    ),
            ) { page ->
                AnimatedPage(pageIndex = page, pagerState = pagerState) {
                    ScreenScaffold {
                        Column(
                            modifier = Modifier.fillMaxSize(),
                            horizontalAlignment = Alignment.CenterHorizontally,
                            verticalArrangement = Arrangement.Center,
                        ) {
                            Text(text = "Page #$page")
                            Spacer(modifier = Modifier.height(8.dp))
                            Text(text = "Swipe up and down")
                        }
                    }
                }
            }
        }
    }
}

占位符

M2.5 和 M3 之间存在一些 API 变更。M3 提供两个占位符修饰符:

如需了解对 Placeholder 组件的其他更改,请参阅下表。

M2.5 M3
PlaceholderState.startPlaceholderAnimation 已被移除
PlaceholderState.placeholderProgression 已被移除
PlaceholderState.isShowContent 替换为 PlaceholderState.isVisible(反转的布尔值)
PlaceholderState.isWipeOff 已被移除
PlaceholderDefaults.painterWithPlaceholderOverlayBackgroundBrush 已被移除
PlaceholderDefaults.placeholderBackgroundBrush 已被移除
PlaceholderDefaults.placeholderChipColors 已被移除

SwipeDismissableNavHost

SwipeDismissableNavHost 是 wear.compose.navigation 的一部分。当此组件与 M3 一起使用时,M3 MaterialTheme 会更新 LocalSwipeToDismissBackgroundScrimColor 和 LocalSwipeToDismissContentScrimColor。

TransformingLazyColumn

TransformingLazyColumn 是 androidx.wear.compose.foundation.lazy 的一部分,可在滚动期间为列表项添加对缩放和变形动画的支持,从而提升用户体验。强烈建议应用从 ScalingLazyColumn 迁移到 TransformingLazyColumn。

与 ScalingLazyColumn 类似,它提供 rememberTransformingLazyColumnState() 来创建可在组合之间记住的 TransformingLazyColumnState。

如需添加缩放和变形动画,请向每个列表项添加以下内容:

  • Modifier.transformedHeight,让您可以使用 TransformationSpec 计算商品的转换后高度,除非您需要进一步自定义,否则可以使用 rememberTransformationSpec()。
  • SurfaceTransformation

如需验证列表顶部和底部的内边距是否正确,请使用 minimumVerticalContentPadding 修饰符。

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,
                )
            }
        }
    }
}

如需详细了解如何在 Compose 中从 M2.5 迁移至 M3,请参阅下面列出的其他资源。

示例

API 参考文档和源代码

设计