Google I/O :Android Jetpack 最新變化(四)Compose

語言: CN / TW / HK

theme: devui-blue highlight: atelier-sulphurpool-light


前言

本系列文章從 Architecture,UI,Performance 和 Compose 這四個方向帶大家瞭解本次 I/O 上 Jetpack 的最新內容。

本文是最終篇:Compose 篇。

今年 I/O 大會上關於 Compose 的主題分享明顯增多了,這也表明了谷歌對於 Compose 推廣之重視。目前 GooglePlay Top1000 的應用中使用 Compose 的已經超過了 100 個,其中不乏一些領域頭部應用,Compose 的穩定性和成熟度也藉機得到了驗證。

讓我們看看 Compose 最新的 1.2 Beta 版本帶來哪些新內容。

1. Material 3

新增的 Compose.M3 庫,可以幫助我們開發符合 Material You 設計規範的的 UI 介面。 groovy 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 類自定義配色方案: ```kotlin 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 中也可以實現這個效果 kotlin // 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 設定的顏色,無論是亮色還是暗色主題,都可以跟隨使用者設定的桌布而變化:

更多參考:https://juejin.cn/post/7064410835422019615

2. Nested Scrolling Interop

Compose 支援與傳統檢視控制元件進行互操作,便於我們階段性的引入 Compose 到專案中。但是在涉及到帶有 Nested Scrolling 事件分發的場景中(例如 CoordinatorLayout ),會發生事件無法正常傳遞的相容性問題,在 1.2 中對於此類問題進行了修復,無論是 CoordinatorLayout 內嵌 Composable , 或者在 Composable 中使用 Scrolling View 控制元件,事件傳遞都會更加平順:

https://android-review.googlesource.com/c/platform/frameworks/support/+/2004590
https://android-review.googlesource.com/c/platform/frameworks/support/+/2038823

3. Downloadable Fonts

Android 8.0(API level 26)起支援了對可下載的谷歌字型的使用,允許通過程式碼動態請求一個非內建字型檔案。在 Compose 1.2 對此功能也進行了支援,注意這個功能需要基於 GMS 服務。 groovy implementation "androidx.compose.ui:ui-text-google-fonts:1.1.1" 使用時,首先使用 FontProvider 定義字型請求資訊 ```kotlin @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 設定每行單元格的數量:

```kotlin 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 可以作為元註解使用,修飾其他自定義註解 ```kotlin @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

```kotlin @Composable fun Activity.rememberWindowSizeClass(): Pair { 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。

```kotlin @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 進行了開源,https://github.com/android/nowinandroid。

開發者通過 App 可以更好地追蹤 Android 最新的技術動向,更重要的是它本身就是一個 Android Jetpack 的最佳實踐,在技術上它具有以下特點: - 基於 Jetpack Compose 實現 UI - 基於 Material3 的視覺樣式和主題 - 對不同尺寸的螢幕進行了支援,能夠自適應佈局 - 整體架構遵循官方文件 UDF 正規化 - 基於 Kotlin Flow 實現響應式程式設計模型 - 遵循 Offline first 設計原則,基於 Room 以及 Proto DataSotre 實現本地資料來源, - 基於 WorkManager 實現遠端/本地資料來源之間的同步

另外,GIthub 上還貼心了附上了架構設計文件,方便你瞭解它的開發思路,Now in Android 已經預定上架 GooglePlay, 相對於 Jetpack 的其他 Demo,它是更加真實和完善,非常值得大家研究和學習。