Google I/O: The latest changes to Android Jetpack (4) Compose

foreword

This series of articles takes you to understand the latest content of Jetpack on this I/O from the four directions of Architecture, UI, Performance and Compose.

This article is the final one: Compose.

The topic sharing about Compose at this year's I/O conference has increased significantly, which also shows that Google attaches great importance to the promotion of Compose. At present, there are more than 100 applications using Compose in the Google Play Top 1000 applications, including some leading applications in the field. The stability and maturity of Compose have also been verified by this opportunity.

Let's see what's new with Compose's latest 1.2 Beta release.

1. Material 3

The newly added Compose.M3 library can help us develop UI interfaces that conform to Material You design specifications.

implementation "androidx.compose.material3:material3:1.0.0-alpha10"
implementation "androidx.compose.material3:material3-window-size-class:1.0.0-alpha10"
复制代码

Material3 emphasizes the personalization and dynamic switching of colors, and Compose.M3 introduces the ColorScheme class to customize the color scheme:

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,
    // 字型
) {
    // 应用内容
}
复制代码

The above is an example of MaterialTheme configuring different theme colors through ColorScheme. You can see that this is not much different from the usage of Colors in Compose.M2, but ColorScheme can define more color slots (Primary, Secondary, Error and other MD color constants), and DynamicColor dynamic color matching can also be supported.

DynamicColor is an important feature of Material3. In Android12 and above devices, the color of the application can be changed with the wallpaper. This effect can now also be achieved in 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 设置的颜色,无论是亮色还是暗色主题,都可以跟随用户设置的壁纸而变化:

更多参考:juejin.cn/post/706441…

2. Nested Scrolling Interop

Compose 支持与传统视图控件进行互操作,便于我们阶段性的引入 Compose 到项目中。但是在涉及到带有 Nested Scrolling 事件分发的场景中(例如 CoordinatorLayout ),会发生事件无法正常传递的兼容性问题,在 1.2 中对于此类问题进行了修复,无论是 CoordinatorLayout 内嵌 Composable , 或者在 Composable 中使用 Scrolling View 控件,事件传递都会更加平顺:

android-review.googlesource.com/c/platform/…
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 进行调整并使之达到稳定。

image.png

以 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 的开发和调试效率。

ezgif.com-gif-maker (19).gif

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 便利的预览功能,同时查看多种屏幕尺寸下的显示效果

Best Practices: Now In Android

Finally, I recommend Now In Android, a new project that Google has just open sourced. Now in Android is Android's official technical blog, sharing technical articles and videos. Now this blog has its own client and is open sourced on Github, github.com/android/now…

Developers can better track the latest Android technical trends through the App, and more importantly, it is an Android Jetpack best practice. Technically, it has the following characteristics:

  • Implement UI based on Jetpack Compose
  • Material3 based visual styles and themes
  • Support for screens of different sizes, able to adapt to the layout
  • The overall architecture follows the official document UDF paradigm
  • Implement reactive programming model based on Kotlin Flow
  • Following the Offline first design principle, the local data source is implemented based on Room and Proto DataSotre.
  • Synchronization between remote/local data sources based on WorkManager

In addition, GIthub also attaches an architecture design document to help you understand its development ideas. Now in Android has been scheduled to be listed on GooglePlay. Compared with other Jetpack demos, it is more realistic and perfect, and it is very worthwhile for everyone to study and learn. .

Guess you like

Origin juejin.im/post/7098146948254793735