プロダクト ニュース

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"))

パフォーマンスの改善

アプリのランタイム パフォーマンスがユーザーにとって非常に重要であることを Google は認識しているため、Compose チームはパフォーマンスを最優先事項として取り組んでいます。このリリースでは、最新バージョンにアップグレードするだけで、さまざまな改善を利用できます。Google の内部スクロール ベンチマークによると、Compose は Views を使用した場合と同等のパフォーマンスを実現しています。

janky.png

さまざまなバージョンの Compose で Views と Jetpack Compose を比較したスクロール パフォーマンスのベンチマーク

Lazy プリフェッチでの一時停止可能なコンポジション

Lazy プリフェッチでの一時停止可能なコンポジションがデフォルトで有効になりました。これは、Compose ランタイムがワークをスケジュールする方法の根本的な変更であり、UI ワークロードの負荷が高い場合のジャンクを大幅に削減するように設計されています。

以前は、コンポジションが開始されると、完了まで実行する必要がありました。コンポジションが複雑な場合、メインスレッドが 1 フレームよりも長くブロックされ、UI がフリーズすることがありました。一時停止可能なコンポジションを使用すると、ランタイムは時間が足りなくなった場合にワークを「一時停止」し、次のフレームでワークを再開できます。これは、Lazy レイアウト プリフェッチと組み合わせて事前にフレームを準備する場合に特に効果的です。Compose 1.9 で導入された Lazy レイアウト CacheWindow API は、より多くのコンテンツをプリフェッチし、一時停止可能なコンポジションを活用して、UI パフォーマンスを大幅に向上させる優れた方法です。

pausable.gif

一時停止可能なコンポジションと Lazy プリフェッチを組み合わせることで、ジャンクを削減できます

また、Modifier.onPlacedModifier.onVisibilityChanged、その他の修飾子の実装を改善し、他の部分のパフォーマンスも最適化しました。Google は、Compose のパフォーマンスの改善に引き続き投資していきます。

新機能

保持

Compose には、さまざまなライフサイクルで状態を保持して管理するための API がいくつか用意されています。たとえば、remember はコンポジション間で状態を保持し、rememberSavable/rememberSerializable はアクティビティまたはプロセスの再作成間で状態を保持します。retain は、これらの API の間に位置する新しい API であり、シリアル化せずに構成変更間で値を保持できますが、プロセスの終了間では保持できません。retain は状態をシリアル化しないため、簡単にシリアル化できないラムダ式、フロー、ビットマップなどの大きなオブジェクトを保持できます。たとえば、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 では、多くの新しいコンポーネントと機能強化が追加されています。

  • TextField に、TextFieldState に基づく試験運用版が追加されました。これにより、テキストの状態をより堅牢に管理できます。また、新しい SecureTextField バリアントと OutlinedSecureTextField バリアントが提供されるようになりました。マテリアルの Text コンポーザブルで自動サイズ調整動作がサポートされるようになりました。
  • カルーセル コンポーネントに、新しい HorizontalCenteredHeroCarousel バリアントが追加されました。
  • TimePicker で、ピッカーモードと入力モードの切り替えがサポートされるようになりました。
  • 縦方向のドラッグハンドルを使用すると、ユーザーはアダプティブ ペインのサイズや位置を変更できます。
centered-hero-carousel.webp

中央揃えの横型ヒーロー カルーセル

Material 3 Expressive API は、material3 ライブラリのアルファ版で引き続き開発されています。詳しくは、こちらの最近の講演をご覧ください。

新しいアニメーション機能

共有要素アニメーションのカスタマイズの更新など、アニメーション API の拡張を続けています。

動的共有要素

デフォルトでは、sharedElement() アニメーションと sharedBounds() アニメーションは、

ターゲット状態に一致するキーが見つかったときにレイアウトの変更をアニメーション化しようとします。ただし、ナビゲーションの方向や現在の UI 状態など、特定の条件に基づいてこのアニメーションを動的に無効にしたい場合があります。

共有要素の遷移が発生するかどうかを制御するために、rememberSharedContentState() に渡される SharedContentConfig をカスタマイズできるようになりました。isEnabled プロパティは、共有要素がアクティブかどうかを決定します。

  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 が使用されていますが、これは本番環境の動作とは異なります。たとえば、エフェクトはキューに登録されるのではなく、すぐに実行されることがあります。今後のリリースでは、本番環境の動作に合わせて、デフォルトで StandardTestDispatcher を使用する新しい API を導入する予定です。新しい動作は、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 デベロッパー向けの最近の追加機能がいくつかあります。

これらのツールの動作については、こちらの最近のデモをご覧ください。

Compose をお楽しみください

Google は、美しいリッチな UI を作成するために必要な API とツールを提供するために、Jetpack Compose に引き続き投資していきます。皆様からのご意見をお待ちしております。これらの変更や、今後追加してほしい機能について、Issue Tracker でフィードバックをお寄せください。

作成者:

続きを読む