移行戦略

既存のビューベースのアプリがある場合、その UI 全体を一度に書き換えたくはないでしょう。このページでは、新しい Compose コンポーネントを既存のアプリに追加する方法について説明します。アプリで Compose の利用を開始するには、既存のアプリに Compose を設定するをご覧ください。

Jetpack Compose は、ビューの相互運用性を最初から考慮して設計されています。この機能で、既存のビューベースのアプリを Compose に移行しつつ、新しい機能を引き続きビルドできます。Compose に移行する場合、アプリが完全に Compose に移行されるまで、コードベースに Compose と View を共存させる増分移行をおすすめします。

ビューベースのアプリの Compose への移行段階
図 1. ビューベースのアプリの Compose への移行段階

アプリを Compose に移行するには、次の手順を行います。

  1. Compose を使用して新しい画面を作成します。
  2. 機能を構築しながら、再利用可能な要素を特定し、一般的な UI コンポーネントのライブラリの作成を開始します。
  3. 既存の機能を 1 画面ずつ置き換えます。

Compose を使用して新しい画面を作成する

Compose を使用した画面全体を含む新しい機能の構築は、Compose の導入を促進する最良の方法です。この戦略で機能を追加することで、企業のビジネスニーズに対応しながら Compose のメリットを活用できます。

Compose で記述された新しい画面
図 2. Compose で記述された新しい画面

Compose を使用して既存のアプリに新しい画面をビルドする場合でも、アプリのアーキテクチャの制約下で作業を行います。フラグメントと Navigation コンポーネントを使用している場合は、新しいフラグメントを作成し、そのコンテンツを Compose に追加する必要があります。

フラグメントで Compose を使用するには、フラグメントの onCreateView() ライフサイクル メソッドで ComposeView を返します。ComposeView には、コンポーズ可能な関数を提供できる setContent() メソッドがあります。

class NewFeatureFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                NewFeatureScreen()
            }
        }
    }
}

詳しくは、フラグメント内の ComposeView をご覧ください。

既存の画面に新しい機能を追加する

ビューと Compose が混在する既存の画面
図 3. ビューと Compose が混在する既存の画面

追加する新機能が既存の画面の一部である場合は、既存のビューベースの画面で Compose を使用することもできます。そのためには、他のビューと同様に、ビュー階層に ComposeView を追加します。

たとえば、LinearLayout に子ビューを追加するとします。これは、次のように XML で行うことができます。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <TextView
      android:id="@+id/text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />

  <androidx.compose.ui.platform.ComposeView
      android:id="@+id/compose_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />
</LinearLayout>

ビューがインフレートしたら、後で階層内の ComposeView を参照して setContent() を呼び出すことができます。

ComposeView について詳しくは、Interoperability API をご覧ください。

一般的な UI コンポーネントのライブラリを作成する

Compose を使用して機能を作成する場合、結果としてコンポーネントのライブラリを構築することになります。そこで、共通の UI コンポーネントのライブラリを作成することで、アプリ内のそれらのコンポーネントの信頼できる唯一の情報源を保持し、再利用性を促進できます。作成した機能は、このライブラリに依存させることができます。この手法は、Compose でカスタム デザイン システムを構築する場合に特に便利です。

アプリのサイズに応じて、このライブラリは別個のパッケージ、モジュール、ライブラリ モジュールにすることができます。アプリ内のモジュールを整理する方法について詳しくは、Android アプリのモジュール化に関するガイドをご覧ください。

既存の機能を Compose で置き換える

Compose を使用して新機能を構築するだけでなく、アプリ内の既存の機能を段階的に移行して Compose を活用することもできます。

アプリを Compose のみにすることで、開発を加速でき、アプリの APK サイズとビルド時間も短縮できます。詳しくは、Compose と View のパフォーマンスを比較するをご覧ください。

シンプルな画面

既存の機能を Compose に移行する際にまず確認する必要があるのは、シンプルな画面です。シンプルな画面には、ウェルカム画面、確認画面、設定画面などがあり、UI に表示されるデータは比較的静的です。

次の XML ファイルを使用します。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <TextView
      android:id="@+id/title_text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/title"
      android:textAppearance="?attr/textAppearanceHeadline2" />

  <TextView
      android:id="@+id/subtitle_text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/subtitle"
      android:textAppearance="?attr/textAppearanceHeadline6" />

  <TextView
      android:id="@+id/body_text"
      android:layout_width="wrap_content"
      android:layout_height="0dp"
      android:layout_weight="1"
      android:text="@string/body"
      android:textAppearance="?attr/textAppearanceBody1" />

  <Button
      android:id="@+id/confirm_button"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="@string/confirm"/>
</LinearLayout>

この XML ファイルは、Compose で次のように書き直すことができます。

@Composable
fun SimpleScreen() {
    Column(Modifier.fillMaxSize()) {
        Text(
            text = stringResource(R.string.title),
            style = MaterialTheme.typography.headlineMedium
        )
        Text(
            text = stringResource(R.string.subtitle),
            style = MaterialTheme.typography.headlineSmall
        )
        Text(
            text = stringResource(R.string.body),
            style = MaterialTheme.typography.bodyMedium
        )
        Spacer(modifier = Modifier.weight(1f))
        Button(onClick = { /* Handle click */ }, Modifier.fillMaxWidth()) {
            Text(text = stringResource(R.string.confirm))
        }
    }
}

ビューと Compose が混在する画面

少量の Compose コードがすでに含まれている画面も、Compose に完全に移行する候補として有力です。画面の複雑さに応じて、完全に Compose に移行することも、部分的に移行することもできます。UI 階層のサブツリーで Compose が実行された画面は、画面全体が Compose になるまで UI 要素の移行が続けられます。このアプローチは、ボトムアップ アプローチとも呼ばれます。

ビューと Compose の混合 UI を Compose に移行するボトムアップ アプローチ
図 4. ビューと Compose の混合 UI を Compose に移行するボトムアップ アプローチ

フラグメントと Navigation コンポーネントを削除する

すべてのフラグメントを削除し、対応する画面レベルのコンポーザブルに置き換えたら、Navigation Compose に移行できます。画面レベルのコンポーザブルには、Compose と View のコンテンツを組み合わせて含めることができますが、Navigation Compose への移行を有効にするには、すべてのナビゲーション デスティネーションをコンポーザブルにする必要があります。それまでは、View と Compose の混在するコードベースでフラグメント ベースの Navigation コンポーネントを引き続き使用する必要があります。詳しくは、Jetpack Navigation を Navigation Compose に移行するをご覧ください。

参考情報

既存のビューベースのアプリを Compose に移行する方法について詳しくは、以下の参考情報をご確認ください。

次のステップ

既存のビューベースのアプリを移行する戦略について説明したので、相互運用 API についてさらに詳しく見ていきましょう。