「リストと詳細」レイアウトを作成する

リストと詳細は、デュアルペイン レイアウトで構成される UI パターンです。一方のペインにアイテムのリストが表示され、もう一方のペインにリストから選択されたアイテムの詳細が表示されます。

このパターンは、大規模なコレクションの要素に関する詳細情報を提供するアプリ(メールのリストと各メール メッセージの詳細なコンテンツを含むメール クライアントなど)に特に役立ちます。リストと詳細は、アプリの設定をカテゴリのリストに分割し、各カテゴリの設定を詳細ウィンドウに表示するなど、重要度の低いパスにも使用できます。

リストページとともに表示される詳細ウィンドウ。
図 1. 十分な画面サイズがある場合は、詳細ペインがリストペインの横に表示されます。
アイテムを選択すると、詳細ウィンドウが画面全体に表示されます。
図 2. 画面サイズが制限されている場合、詳細ウィンドウ(アイテムが選択されているため)がスペース全体を占有します。

NavigableListDetailPaneScaffold を使用してリストと詳細パターンを実装する

NavigableListDetailPaneScaffold は、Jetpack Compose でのリストと詳細レイアウトの実装を簡素化するコンポーザブルです。ListDetailPaneScaffold をラップし、組み込みのナビゲーションと予測型「戻る」アニメーションを追加します。

リストと詳細スキャフォールドは、最大 3 つのペインをサポートします。

  1. リストペイン: アイテムのコレクションを表示します。
  2. 詳細ウィンドウ: 選択したアイテムの詳細が表示されます。
  3. 追加ペイン(省略可): 必要に応じて追加のコンテキストを提供します。

スキャフォールドはウィンドウ サイズに基づいて調整されます。

  • 大きなウィンドウでは、リストペインと詳細ウィンドウが並んで表示されます。
  • 小さいウィンドウでは、一度に 1 つのペインのみが表示され、ユーザーが移動すると切り替わります。

依存関係の宣言

NavigableListDetailPaneScaffold は、マテリアル 3 アダプティブ ナビゲーション ライブラリの一部です。

アプリまたはモジュールの build.gradle ファイルに、次の 3 つの関連する依存関係を追加します。

Kotlin

implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")

Groovy

implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
  • adaptive: HingeInfo や Posture などの低レベルの構成要素
  • adaptive-layout: ListDetailPaneScaffold や SupportingPaneScaffold などのアダプティブ レイアウト
  • adaptive-navigation: ペイン内およびペイン間のナビゲーション用のコンポーザブル。デフォルトでナビゲーションをサポートするアダプティブ レイアウト(NavigableListDetailPaneScaffold、NavigableSupportingPaneScaffold など)も含まれます。

プロジェクトに compose-material3-adaptive バージョン 1.1.0-beta1 以降が含まれていることを確認します。

予測型「戻る」ジェスチャーをオプトインする

Android 15 以前で予測型「戻る」アニメーションを有効にするには、予測型「戻る」ジェスチャーのサポートを有効にする必要があります。オプトインするには、AndroidManifest.xml ファイル内の <application> タグまたは個々の <activity> タグに android:enableOnBackInvokedCallback="true" を追加します。詳細については、予測型「戻る」ジェスチャーを有効にするをご覧ください。

アプリが Android 16(API レベル 36)以降をターゲットにしている場合、予測型「戻る」はデフォルトで有効になります。

基本的な使用方法

次のように NavigableListDetailPaneScaffold を実装します。

  1. 選択したコンテンツを表すクラスを使用します。Parcelable クラスを使用して、選択したリストアイテムの保存と復元をサポートします。kotlin-parcelize プラグインを使用してコードを生成します。
  2. rememberListDetailPaneScaffoldNavigator で ThreePaneScaffoldNavigator を作成します。

このナビゲーターは、リストペイン、詳細ペイン、追加ペイン間の移動に使用されます。汎用型を宣言することで、ナビゲーターはスキャフォールドの状態(つまり、表示されている MyItem)も追跡します。このタイプは Parcelable であるため、ナビゲーターによって状態を保存、復元して、構成の変更を自動的に処理できます。

  1. ナビゲータを NavigableListDetailPaneScaffold コンポーザブルに渡します。

  2. リストペインの実装を NavigableListDetailPaneScaffold に提供します。AnimatedPane を使用して、ナビゲーション中にデフォルトのペイン アニメーションを適用します。次に、ThreePaneScaffoldNavigator を使用して詳細ウィンドウ ListDetailPaneScaffoldRole.Detail に移動し、渡されたアイテムを表示します。

  3. 詳細ウィンドウの実装を NavigableListDetailPaneScaffold に含めます。

ナビゲーションが完了すると、currentDestination には、アプリが移動したペイン(ペインに表示されるコンテンツを含む)が含まれます。contentKey プロパティは、元の呼び出しで指定されたものと同じ型であるため、表示する必要があるデータにアクセスできます。

  1. 必要に応じて、NavigableListDetailPaneScaffold の defaultBackBehavior を変更します。デフォルトでは、NavigableListDetailPaneScaffold は defaultBackBehavior に PopUntilScaffoldValueChange を使用します。

アプリで別の「戻る」ナビゲーション パターンが必要な場合は、別の BackNavigationBehavior オプションを指定することで、この動作をオーバーライドできます。

BackNavigationBehavior オプション

次のセクションでは、一方のペインにメールのリスト、もう一方のペインに詳細ビューを表示するメールアプリの例を使用します。

この動作は、レイアウト構造全体の変更に重点を置いています。マルチペイン設定では、詳細ウィンドウでメールの内容を変更しても、基盤となるレイアウト構造は変更されません。そのため、戻るボタンを押すと、現在のコンテキスト内で元に戻すレイアウト変更がないため、アプリまたは現在のナビゲーション グラフが終了する可能性があります。シングルペイン レイアウトでは、[戻る] を押すと、詳細ビュー内のコンテンツの変更がスキップされ、リスト表示に戻ります。これは、明確なレイアウト変更を表します。

以下の例を考えてみましょう。

  • マルチペイン: 詳細ウィンドウでメール(アイテム 1)を表示しています。別のメール(アイテム 2)をクリックすると、詳細ウィンドウが更新されますが、リストペインと詳細ウィンドウは表示されたままになります。戻るボタンを押すと、アプリまたは現在のナビゲーション フローが終了する可能性があります。
  • シングルペイン: アイテム 1 を表示してからアイテム 2 を表示し、戻るボタンを押すと、メールリスト ペインに直接戻ります。

これは、ユーザーが戻る操作ごとに個別のレイアウト遷移を認識できるようにする場合に使用します。

さまざまなレイアウト ペインでナビゲーション値が変化する様子を示すイラスト。
PopUntilContentChange

この動作では、表示されるコンテンツが優先されます。アイテム 1 を表示してからアイテム 2 を表示した場合、戻るボタンを押すと、レイアウトに関係なくアイテム 1 に戻ります。

以下の例を考えてみましょう。

  • マルチペイン: 詳細ウィンドウでアイテム 1 を表示し、リストでアイテム 2 をクリックします。詳細ウィンドウが更新されます。戻るを押すと、詳細ウィンドウがアイテム 1 に戻ります。
  • シングルペイン: 同じコンテンツの復元が発生します。

これは、ユーザーが戻る操作で以前に表示したコンテンツに戻ることを想定している場合に使用します。

2 つの詳細ウィンドウ間の遷移
PopUntilCurrentDestinationChange

この動作により、現在のナビゲーション デスティネーションが変更されるまで、バックスタックがポップされます。これは、シングルペイン レイアウトとマルチペイン レイアウトの両方に当てはまります。

以下の例を考えてみましょう。

シングルペイン レイアウトでもマルチペイン レイアウトでも、戻るボタンを押すと、フォーカスは常にハイライト表示されたナビゲーション要素から前のデスティネーションに移動します。メールアプリでは、選択したペインの視覚的な表示が切り替わります。

現在のナビゲーションを明確に視覚的に示すことがユーザー エクスペリエンスにとって重要である場合に使用します。

詳細ペインとリストペイン間の移動
PopLatest

このオプションでは、バックスタックから最新のデスティネーションのみが削除されます。このオプションは、中間状態をスキップせずに「戻る」ナビゲーションを行う場合に使用します。

これらのステップを実装すると、コードは次のようになります。

NavigableListDetailPaneScaffold(
    navigator = navigator,
    listPane = {
        AnimatedPane {
            ListContent(
                words = sampleWords,
                selectionState = navigator.currentDestination?.contentKey?.let {
                    SelectionVisibilityState.ShowSelection(it)
                } ?: SelectionVisibilityState.NoSelection,
                onWordClick = { word ->
                    scope.launch {
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, word)
                    }
                },
                animatedVisibilityScope = this@AnimatedPane,
                sharedTransitionScope = this@SharedTransitionLayout
            )
        }
    },
    detailPane = {
        AnimatedPane {
            DetailContent(
                definedWord = navigator.currentDestination?.contentKey,
                animatedVisibilityScope = this@AnimatedPane,
                sharedTransitionScope = this@SharedTransitionLayout,
                onClosePane = {
                    scope.launch {
                        navigator.navigateBack(
                            backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange
                        )

                    }
                }
            )
        }
    }