產品新訊

Jetpack Compose 2025 年 12 月發布版本的新功能

6 分鐘小故事
Nick Butcher
產品經理

我們很高興宣布,Jetpack Compose 2025 年 12 月版現已推出穩定版。這個版本包含核心 Compose 模組 1.10 版和 Material 3 1.4 版 (請參閱完整的 BOM 對應),新增了多項功能,並大幅提升效能。

如要使用今天的版本,請將 Compose BOM 版本升級至 2025.12.00

implementation(platform("androidx.compose:compose-bom:2025.12.00"))

提升效能

我們瞭解應用程式的執行階段效能對您和使用者來說非常重要,因此效能一直是 Compose 團隊的首要任務。這個版本帶來多項改善,只要升級至最新版本,即可享有所有優勢。根據內部捲動基準,Compose 現在的效能與使用 Views 時的效能相同:

janky.png

捲動效能基準,比較不同 Compose 版本中 Views 和 Jetpack Compose 的效能

延遲預先擷取時可暫停組合

系統現在預設啟用延遲預先擷取中的可暫停組合。這項重大變更會影響 Compose 執行階段排程的工作方式,目的是大幅減少高負載 UI 工作負載期間的卡頓。

先前,組成作業一旦開始,就必須執行完畢。如果組成作業很複雜,可能會阻斷主執行緒的時間超過單一影格,導致 UI 凍結。有了可暫停的組成作業,如果執行時間即將用盡,執行階段現在可以「暫停」作業,並在下一個影格中繼續作業。搭配延遲版面配置預先擷取功能使用時,效果特別顯著,可預先準備影格。Compose 1.9 推出的延遲版面配置 CacheWindow API 是預先擷取更多內容的絕佳方式,可搭配可暫停的組成作業,大幅提升 UI 效能。

pausable.gif

可暫停的組合結構搭配 Lazy 預先擷取,有助於減少卡頓

我們也針對其他部分進行效能最佳化,包括改良 Modifier.onPlacedModifier.onVisibilityChanged 和其他修飾符實作項目。我們會持續投入心力提升 Compose 的效能。

新功能

保留

Compose 提供多種 API,可跨不同生命週期保留及管理狀態;舉例來說,remember 會在組合之間保留狀態,而 rememberSavable/rememberSerializable 則會在活動或程序重建期間保留狀態。retain 是介於這些 API 之間的新 API,可讓您在設定變更期間保留值,但不會在程序終止時保留。由於 retain 不會序列化狀態,因此您可以保留 Lambda 運算式、Flow 和點陣圖等大型物件,這些物件不容易序列化。舉例來說,您可以使用 retain 管理媒體播放器 (例如 ExoPlayer),確保媒體播放不會因設定變更而中斷。

@Composable

fun MediaPlayer() {

    val applicationContext = LocalContext.current.applicationContext

    val exoPlayer = retain { ExoPlayer.Builder(applicationContext).apply { ... }.build() }

    ...

}

我們衷心感謝 AndroidDev 社群 (尤其是 Circuit 團隊),他們對這項功能的設計有深遠影響,並做出許多貢獻。

Material 1.4

material3 程式庫 1.4.0 版新增多項元件和強化功能:

centered-hero-carousel.webp

水平置中主頁橫幅輪轉介面

請注意,Material 3 Expressive API 會持續在 material3 程式庫的 Alpha 版中開發。如要瞭解詳情,請參閱這場最近的演講:

全新動畫功能

我們持續擴充動畫 API,包括更新共用元素動畫的自訂功能。

動態共用元素

根據預設,sharedElement()sharedBounds() 動畫會嘗試為

每當在目標狀態中找到相符的鍵時,版面配置就會變更。不過,您可能想根據特定條件 (例如導覽方向或目前的 UI 狀態),動態停用這項動畫。

如要控管是否發生共用元素轉場效果,現在可以自訂傳遞至 rememberSharedContentState()SharedContentConfigisEnabled 屬性會決定共用元素是否有效。

SharedTransitionLayout {

        val transition = updateTransition(currentState)

        transition.AnimatedContent { targetState ->

            // Create the configuration that depends on state changing.

            fun animationConfig() : SharedTransitionScope.SharedContentConfig {

                return object : SharedTransitionScope.SharedContentConfig {

                    override val SharedTransitionScope.SharedContentState.isEnabled: Boolean

                        get() =

                            // determine whether to perform a shared element transition

                }

            }

}

詳情請參閱說明文件

Modifier.skipToLookaheadPosition()

這個版本新增了 Modifier.skipToLookaheadPosition() 輔助鍵,可在執行共用元素動畫時,保留可組合項的最終位置。這樣就能執行「顯示」類型動畫等轉場效果,如 Androidify 範例中相機的漸進式顯示效果。如要瞭解詳情,請觀看這部影片:

共用元素轉場效果的初速度

本次發布內容新增了共用元素轉場效果 API prepareTransitionWithInitialVelocity,可讓您將初始速度 (例如來自手勢) 傳遞至共用元素轉場效果:

Modifier.fillMaxSize()

    .draggable2D(

        rememberDraggable2DState { offset += it },

        onDragStopped = { velocity ->

            // Set up the initial velocity for the upcoming shared element

            // transition.

            sharedContentStateForDraggableCat

                ?.prepareTransitionWithInitialVelocity(velocity)

            showDetails = false

        },

    )
fling-shared.gif

從手勢開始,以初始速度進行共用元素轉換

遮蓋轉場效果

EnterTransitionExitTransition 會定義 AnimatedVisibility/AnimatedContent 可組合函式的顯示或消失方式。新的實驗性遮罩選項可讓您指定遮罩或遮蓋內容的顏色,例如在內容上淡入/淡出半透明黑色圖層:

veil_2.gif

隱藏式動畫內容:請注意,動畫播放期間,格線內容會覆蓋半透明的遮罩 (或幕布)

AnimatedContent(

    targetState = page,

    modifier = Modifier.fillMaxSize().weight(1f),

    transitionSpec = {

        if (targetState > initialState) {

            (slideInHorizontally { it } togetherWith

                    slideOutHorizontally { -it / 2 } + veilOut(targetColor = veilColor))

        } else {

            slideInHorizontally { -it / 2 } +

                    unveilIn(initialColor = veilColor) togetherWith slideOutHorizontally { it }

        }

    },

) { targetPage ->

    ...

}

近期異動內容

Modifier.onFirstVisible 已淘汰

Compose 1.9 導入了 Modifier.onVisibilityChangedModifier.onFirstVisible。審查您的意見回饋後,我們發現無法確定是否能遵守 Modifier.onFirstVisible 的合約,具體來說,就是無法確定項目何時會「首次」顯示。舉例來說,如果項目捲動到檢視區塊外,Lazy 版面配置可能會捨棄這些項目,然後在項目捲動回檢視區塊時再次組合。在這種情況下,由於這是新編寫的項目,因此 onFirstVisible 回呼會再次觸發。返回含有 onFirstVisible 的先前瀏覽畫面時,也會發生類似行為。因此,我們決定在下一個 Compose 版本 (1.11) 中淘汰這個修飾符,並建議遷移至 onVisibilityChanged。詳情請參閱說明文件

測試中的協同程式調度

我們計畫變更測試中的協同程式調度,以減少測試不穩定性並找出更多問題。目前測試使用 UnconfinedTestDispatcher,這與實際運作行為不同;舉例來說,效果可能會立即執行,而不是排入佇列。在日後的版本中,我們計畫推出新的 API,預設使用 StandardTestDispatcher 來配合實際運作行為。您現在可以在 1.10 中試用新行為:

@get:Rule // also createAndroidComposeRule, createEmptyComposeRule

val rule = createComposeRule(effectContext = StandardTestDispatcher())

使用 StandardTestDispatcher 會將工作排入佇列,因此您必須使用 composeTestRule.waitForIdle()composeTestRule.runOnIdle() 等同步機制。如果測試使用 runTest,請務必確保 runTest 和 Compose 規則共用相同的 StandardTestDispatcher 執行個體,以進行同步處理。

// 1. Create a SINGLE dispatcher instance

val testDispatcher = StandardTestDispatcher()



// 2. Pass it to your Compose rule

@get:Rule

val composeRule = createComposeRule(effectContext = testDispatcher)



@Test

// 3. Pass the *SAME INSTANCE* to runTest

fun myTest() = runTest(testDispatcher) {

    composeRule.setContent { /* ... */ }

}

工具

優質的 API 值得搭配優質的工具,而 Android Studio 最近也為 Compose 開發人員新增了多項功能:

如要查看這些工具的實際應用方式,請觀看這場近期的示範:

祝您編寫愉快!

我們將持續投入 Jetpack Compose,為您提供建立豐富優美 UI 所需的 API 和工具。我們很重視您的意見,歡迎透過問題追蹤器分享對這些變更的看法,或提出您希望我們接下來推出的功能。

繼續閱讀