在 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.8.0-alpha03")

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

Wear Compose Foundation 库 1.8.0-alpha03 版引入了一些新组件,这些组件旨在与 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.8.0-alpha03")
implementation("androidx.wear.compose:compose-navigation:1.8.0-alpha03")

主题

在 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 不同,它包含以下功能:

  • 九种全新文本样式
  • Flex 字体,可针对不同的字重、字宽和圆角自定义字体比例
  • AnimatedText,该应用使用 Flex 字体

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 字体

借助 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 中均具有可用性。不过,M2.5 中没有一些 M3 组件和布局。此外,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.8.0-alpha03
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)在应用内转换期间(例如滑动关闭)保持可见。它为主要应用内容提供了一个槽位,该槽位通常由导航组件(例如 SwipeDismissableNavHost)提供

您为 Activity 声明一个 AppScaffold,并为每个屏幕使用一个 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 是否正在分页(由 PagerState 确定)来协调显示和隐藏 TimeText 和 HorizontalPageIndicator。

此外,还有一个新的 AnimatedPage 组件,它会根据 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 参考文档和源代码

设计