在 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-rc01")

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

Wear Compose Foundation 库版本 1.7.0-rc01 引入了一些旨在与 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-rc01")
implementation("androidx.wear.compose:compose-navigation:1.7.0-rc01")

主题

在 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
Color 已重命名为 ColorScheme
13 种颜色 28 种颜色
不适用 新的动态配色主题
不适用 新增了三级颜色,让您更具表现力

动态配色主题

动态配色主题是 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 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.animatedShape 会在按下时为图标按钮添加动画效果
IconToggleButton IconToggleButtonDefaults.animatedShape 会在按下时为图标切换按钮添加动画效果,而
IconToggleButtonDefaults.variantAnimatedShapes 会在按下和选中/取消选中时为图标切换按钮添加动画效果
TextButton TextButtonDefaults.animatedShape 会在按下时为文字按钮添加动画效果
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.IconButtonandroidx.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.CheckboxButtonandroidx.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 中的 TextIcon 不再使用它
androidx.wear.compose.material.PositionIndicator androidx.wear.compose.material3.ScrollIndicator
androidx.wear.compose.material.RadioButton 无 M3 等效项,请迁移到 androidx.wear.compose.material3.RadioButtonandroidx.wear.compose.material3.SplitRadioButton
androidx.wear.compose.material.SwipeToRevealCard androidx.wear.compose.material3.SwipeToReveal
androidx.wear.compose.material.SwipeToRevealChip androidx.wear.compose.material3.SwipeToReveal
android.wear.compose.material.Scaffold androidx.wear.compose.material3.AppScaffoldandroidx.wear.compose.material3.ScreenScaffold
androidx.wear.compose.material.SplitToggleChip 没有 M3 等效项,请迁移到 androidx.wear.compose.material3.SplitCheckboxButtonandroidx.wear.compose.material3.SplitSwitchButtonandroidx.wear.compose.material3.SplitRadioButton
androidx.wear.compose.material.Switch 没有 M3 等效项,请迁移到 androidx.wear.compose.material3.SwitchButtonandroidx.wear.compose.material3.SplitSwitchButton
androidx.wear.compose.material.ToggleButton androidx.wear.compose.material3.IconToggleButtonandroidx.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 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 android.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 android.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.SwipeToRevealCardandroidx.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-rc01
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 实现为 TextmaxLinestextAlign 提供默认值。这些默认值可在 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 引入了一个新按钮:EdgeButtonEdgeButton 有 4 种不同的尺寸:超小号、小号、中号和大号。EdgeButton 实现根据大小为 maxLines 提供默认值,该值可以自定义。

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

val state = rememberTransformingLazyColumnState()
ScreenScaffold(
    scrollState = state,
    contentPadding =
        rememberResponsiveColumnPadding(
            first = ColumnItemType.ListHeader
        ),
    edgeButton = {
        EdgeButton(
            onClick = { }
        ) {
            Text(stringResource(R.string.show))
        }
    }
){ contentPadding ->
    TransformingLazyColumn(state = state, contentPadding = contentPadding,){
        // additional code here
    }
}

Scaffold

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

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

您为 Activity 声明一个 AppScaffold,并为每个 Screen 使用一个 ScreenScaffoldAppScaffold 会向屏幕添加默认的 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 padding = rememberResponsiveColumnPadding(
        first = ColumnItemType.BodyText
    )

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

如果您将 HorizontalPagerHorizontalPagerIndicator 搭配使用,则可以迁移到 HorizontalPagerScaffoldHorizontalPagerScaffold 放置在 AppScaffold 内。AppScaffoldHorizontalPagerScaffold 会布局 Pager 的结构,并协调 HorizontalPageIndicatorTimeText 组件的转换。

HorizontalPagerScaffold 默认会在屏幕中心位置显示 HorizontalPageIndicator,并根据 Pager 是否正在分页来协调显示和隐藏 TimeTextHorizontalPageIndicator,这由 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 变更。 Placeholder.PlaceholderDefaults 现在提供两个修饰符:

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

M2.5 M3
PlaceholderState.startPlaceholderAnimation 已被移除
PlaceholderState.placeholderProgression 已被移除
PlaceholderState.isShowContent 已重命名为 !PlaceholderState.isVisible
PlaceholderState.isWipeOff 已被移除
PlaceholderDefaults.painterWithPlaceholderOverlayBackgroundBrush 已被移除
PlaceholderDefaults.placeholderBackgroundBrush 已被移除
PlaceholderDefaults.placeholderChipColors 已被移除

SwipeDismissableNavHost

SwipeDismissableNavHostwear.compose.navigation 的一部分。当此组件与 M3 搭配使用时,M3 MaterialTheme 会更新 LocalSwipeToDismissBackgroundScrimColorLocalSwipeToDismissContentScrimColor

TransformingLazyColumn

TransformingLazyColumnwear.compose.lazy.foundation 的一部分,在滚动期间添加了对列表项的缩放和变形动画的支持,从而提升用户体验。强烈建议应用从 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 参考文档和源代码

设计