1. Material 3
新增的 Compose.M3 库,可以帮助我们开发符合 Material You 设计规范的的 UI 界面。
implementation "androidx.compose.material3:material3:1.0.0-alpha10" implementation "androidx.compose.material3:material3-window-size-class:1.0.0-alpha10"
Material3 强调颜色的个性化和动态切换,Compose.M3 引入 ColorScheme 类自定义配色方案:
val AppLightColorScheme = lightColorScheme ( primary = Color(...), // secondary、tertiary 等等 // 具有浅色基准值的 ColorScheme 实例 ) val AppDarkColorScheme = darkColorScheme( // primary、secondary、tertiary 等等 // 具有深色基准值的 ColorScheme 实例 ) val dark = isSystemInDarkTheme() val colorScheme = if (dark) AppDarkColorScheme else AppLightColorScheme // 将 colorScheme 作为参数传递给 MaterialTheme。 MaterialTheme ( colorScheme = colorScheme, // 字型 ) { // 应用内容 }
上面是 MaterialTheme 通过 ColorScheme 配置不同主题颜色的例子,可以看到这与 Compose.M2 中 Colors 用法区别不大, 但是 ColorScheme 可定义的颜色槽(Primary,Secondary,Error 等MD颜色常量)种类更多,而且还可以支持 DynamicColor 动态配色。
DynamicColor 是 Material3 的重要特色,在 Android12 及以上设备中,可以实现应用的颜色跟随壁纸变化。如今 Compose 中也可以实现这个效果
// Dynamic color is available on Android 12+ val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S val colorScheme = when { dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current) dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current) darkTheme -> DarkColorScheme else -> LightColorScheme }
如上,Compose 通过 dynamicXXXColorScheme 设置的颜色,无论是亮色还是暗色主题,都可以跟随用户设置的壁纸而变化:
2. Nested Scrolling Interop
Compose 支持与传统视图控件进行互操作,便于我们阶段性的引入 Compose 到项目中。但是在涉及到带有 Nested Scrolling 事件分发的场景中(例如 CoordinatorLayout ),会发生事件无法正常传递的兼容性问题,在 1.2 中对于此类问题进行了修复,无论是 CoordinatorLayout 内嵌 Composable , 或者在 Composable 中使用 Scrolling View 控件,事件传递都会更加平顺:
android-review.googlesource.com/c/platform/…
3. Downloadable Fonts
Android 8.0(API level 26)起支持了对可下载的谷歌字体的使用,允许通过代码动态请求一个非内置字体文件。在 Compose 1.2 对此功能也进行了支持,注意这个功能需要基于 GMS 服务。
implementation "androidx.compose.ui:ui-text-google-fonts:1.1.1"
使用时,首先使用 FontProvider 定义字体请求信息
@OptIn(ExperimentalTextApi::class) val provider = GoogleFont.Provider( providerAuthority = "com.google.android.gms.fonts", providerPackage = "com.google.android.gms", certificates = R.array.com_google_android_gms_fonts_certs ) 然后,使用此 Provider 定义 FontFamily,接着在 Composable 应用即可, val fontName = GoogleFont(“Lobster Two”) val fontFamily = FontFamily( Font(googleFont = GoogleFont(name), fontProvider = provider) ) Text( fontFamily = fontFamily, text = "Hello World!" )
4. Lazy Grid
Compose 1.2 中进一步优化了 LazyRow 和 LazyColumn 的性能,并在此基础上新增了 LazyGrid 用来实现需求中常见的网格布局效果。Lazy Grid 在 1.0.2 就已经引入,如今 1.2 中对 API 进行调整并使之达到稳定。
以 LazyVerticalGrid 为例,我们可以通过 GridCells.Fixed 设置每行单元格的数量:
val data = listOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5") LazyVerticalGrid( columns = GridCells.Fixed(3), contentPadding = PaddingValues(8.dp) ) {//this: LazyGridScope items(data.size) { index -> Card( modifier = Modifier.padding(4.dp), backgroundColor = Color.LightGray ) { Text( text = data[index], textAlign = TextAlign.Center ) } } }
此外,也可以通过 GridCells.Adaptive() 通过制定单元格大小决定每行的数量。此时,所有单元格都会以 Adaptive 中的值设置统一的 width。
LazyGridScope 像 LazyListScope 一样也提供了 item, items, itemsIndexed 等方法布局子项。另外 LazyGridState 中的方法也基本上对齐了 LazyListState。
5. Tools
在工具方面,Android Studio 为 Compose 的开发调试提供了更多实用功能。
@Preview & Live Edit
1.2.0 中的 @Preview 可以作为元注解使用,修饰其他自定义注解
@Preview(showBackground = true) @Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES) annotation class MyDevices() @MyDevices @Composable fun Greeting() { ... }
如上,我们可以通过自定义注解可以复用 @Preview 中的各种配置,减少为了预览而写的模板代码。
说到预览,Android Studio 一直致力于提升预览效率,Android Studio Arctic Fox 曾引入 Live literals 功能,对于代码中 Int,String,Color,Dp,Boolean 等常见类型的字面值的修改,无需编译即可在预览画面中实时更新。本次大会上带来了升级版的 Live Edit,它需要使用最新的 Android Studio Electric Eel 中开启。不仅仅是字面值,它可以让任意代码的修改(函数签名变动之类的修改不行),在预览窗口或者你的设备上立即生效,几乎实现了前端一般的开发体验,是本次大会令我惊喜的功能,它将大幅提高 Compose 的开发和调试效率。
Layout Inspector & Recomposition Counts
我们在传统视图开发中经常使用 Layout Inspector 观察视图结构, Compose 虽然基于 Composable 函数构建 UI ,但同样也得到了 layout Inspector 的支持,它可以帮助我们查看 Composition 视图树的布局。
此外,本次 I/O 还介绍了 Layout Inspector 的一个新功能 Recomposition Counts,我们知道不必要的重组会拖慢 Compose UI 的刷新性能,借助这个新工具,我们可以在 IDE 中调试和观察 Composable 重组次数,帮助我们及时发现和优化不符合预期的多余重组。
Animation Preview
Android Studio 增加了对 Compose 动画效果实时预览。在动画预览窗口中,每个动画的状态值会以多轨道的形式呈现,我们可以查看特定时间点下的每个动画值的确切值,并且可以暂停、循环播放动画、快进或放慢动画,以便在动画过渡过程中调试动画。
Compose 的动画 API 数量众多,目前并非所有的 API 都支持预览,IDE 会自动检查代码中可以进行预览的动画,并添加 Start Animation Inspection 图标,便于开发者发现和使用
6. 适应多种屏幕尺寸
Compose 正逐渐成为 Android 的首选 UI 开发方案,所以为了覆盖尽可能多的使用场景,Compose 第一时间对各种屏幕尺寸下(手机,平板,电脑,折叠屏)的 UI 开发进行了支持。 在具体开发中,我们需要先定义 WindowSizeClass 对各种屏幕类型分类,推荐分为三类:
当屏幕尺寸因为设备折叠等发生变化时,Compose 会自动响应 onConfigurationChanged 触发重组,重组中我们根据当前屏幕尺寸转换为对应的 WindowSizeClass
@Composable fun Activity.rememberWindowSizeClass(): Pair<WindowWidthSizeClass, WindowHeightSizeClass> { val configuration = LocalConfiguration.current val windowMetrics = remember(configuration) { WindowMetricsCalculator.getOrCreate() .computeCurrentWindowMetrics(this) } val windowDpSize = with(LocalDensity.current) { windowMetrics.bounds.toComposeRect().size.toDpSize() } val widthWindowSizeClass = when { windowDpSize.width < 600.dp -> WindowWidthSizeClass.Compact windowDpSize.width < 840.dp -> WindowWidthSizeClass.Medium else -> WindowWidthSizeClass.Expanded } val heightWindowSizeClass = when { windowDpSize.height < 480.dp -> WindowHeightSizeClass.Compact windowDpSize.height < 900.dp -> WindowHeightSizeClass.Medium else -> WindowHeightSizeClass.Expanded } return widthWindowSizeClass to heightWindowSizeClass }
接下来,我们就可以面向 WindowSizeClass 进行 Composable 布局了,这样做的好处是,无需关心具体的 width/height 数值,更不需要关心当前设备类型是平板还是手机,因为未来,硬件种类的界限将越来越模糊,所以最合理的分类方式是 WindowSizeClass。
@Composable fun MyApp(widthSizeClass: WindowWidthSizeClass) { // 非 Compact 类型屏幕时,不显示 AppBar val showTopAppBar = widthSizeClass != WindowWidthSizeClass.Compact // MyScreen 不依赖 WindowSizeClass,只需要知道是否显示 showTopAppBar,关注点分离 MyScreen( showTopAppBar = showTopAppBar, /* ... */ ) }
当然我们可以使用 Android Studio 便利的预览功能,同时查看多种屏幕尺寸下的显示效果
最佳实践: Now In Android
最后推荐一个谷歌刚刚开源的新项目 Now In Android。 Now in Android 是 Android 官方的技术博客,分享技术文章和视频,如今这个博客有了自己的客户端,并在 Github 进行了开源,github.com/android/now…
开发者通过 App 可以更好地追踪 Android 最新的技术动向,更重要的是它本身就是一个 Android Jetpack 的最佳实践,在技术上它具有以下特点:
- 基于 Jetpack Compose 实现 UI
- 基于 Material3 的视觉样式和主题
- 对不同尺寸的屏幕进行了支持,能够自适应布局
- 整体架构遵循官方文档 UDF 范式
- 基于 Kotlin Flow 实现响应式编程模型
- 遵循 Offline first 设计原则,基于 Room 以及 Proto DataSotre 实现本地数据源,
- 基于 WorkManager 实现远程/本地数据源之间的同步
另外,GIthub 上还贴心了附上了架构设计文档,方便你了解它的开发思路,Now in Android 已经预定上架 GooglePlay, 相对于 Jetpack 的其他 Demo,它是更加真实和完善,非常值得大家研究和学习。