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

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

このパターンは、大規模なコレクションの要素に関する詳細情報を提供するアプリケーションに特に便利です。たとえば、メールのリストと各メール メッセージの詳細なコンテンツがあるメール クライアントなどです。リスト / 詳細は、重要度の低いパスにも使用できます。たとえば、アプリの設定をカテゴリのリストに分割し、各カテゴリの設定を詳細ペインに表示する場合などです。

ListDetailPaneScaffold で UI パターンを実装する

ListDetailPaneScaffold は、アプリでのリスト / 詳細パターンの実装を簡素化するコンポーザブルです。リスト / 詳細スキャフォールドは最大 3 つのペイン(リストペイン、詳細ペイン、オプションの追加ペイン)で構成できます。スキャフォールドは画面スペースの計算を処理します。十分な画面サイズを使用できる場合、詳細ペインがリストペインと並んで表示されます。小さい画面サイズの場合、スキャフォールドは自動的にリストペインまたは詳細ペインの全画面表示に切り替わります。

リストページの横に表示される詳細ペイン。
図 1. 十分な画面サイズを使用できる場合、詳細ペインがリストペインと並んで表示されます。
項目を選択すると、詳細ペインが画面全体に表示されます。
図 2. 画面サイズが限られている場合は、アイテムが選択されているため、詳細ペインがスペース全体に表示されます。

依存関係の宣言

ListDetailPaneScaffold は、マテリアル 3 アダプティブ レイアウト ライブラリの一部です。

アプリには、3 つの関連するマテリアル 3 ライブラリの依存関係を含める必要があります。

  • 適応型 - 下位の構成要素(HingeInfoPosture など)
  • adaptive-layout - ListDetailPaneScaffoldSupportingPaneScaffold などのアダプティブ レイアウト
    • adaptive-navigation - ペイン内やペイン間のナビゲーション用のコンポーザブル

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

Kotlin


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

Groovy


implementation 'androidx.compose.material3.adaptive:adaptive:1.0.0-alpha12'
implementation 'androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha12'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha12'

基本的な使用方法

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

  1. 選択対象のコンテンツを表すクラスを使用する。選択したリストアイテムの保存と復元をサポートするには、このクラスを Parcelable にする必要があります。kotlin-parcelize プラグインを使用してコードを生成します。

    @Parcelize
    class MyItem(val id: Int) : Parcelable

  2. rememberListDetailPaneScaffoldNavigator を使用して ThreePaneScaffoldNavigator を作成し、BackHandler を追加します。このナビゲータは、リスト、詳細、追加ペイン間を移動するために使用されます。汎用型を宣言することで、ナビゲータはスキャフォールドの状態(つまり、MyItem が表示されている)も追跡します。このタイプは Parcelable であるため、ナビゲータで状態を保存および復元して、構成の変更を自動的に処理できます。BackHandler は、システムの「戻る」ジェスチャーまたはボタンを使用した戻る操作をサポートしています。ListDetailPaneScaffold の [戻る] ボタンの想定される動作は、ウィンドウ サイズと現在のスキャフォールド値によって異なります。ListDetailPaneScaffold が現在の状態に戻ることをサポートできる場合、canNavigateBack()true であり、BackHandler を有効にします。

    val navigator = rememberListDetailPaneScaffoldNavigator<MyItem>()
    
    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

  3. scaffoldStatenavigator から ListDetailPaneScaffold コンポーザブルに渡します。

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        // ...
    )

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

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane {
                MyList(
                    onItemClick = { item ->
                        // Navigate to the detail pane with the passed item
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, item)
                    }
                )
            }
        },
        // ...
    )

  5. 詳細ペインの実装を ListDetailPaneScaffold に含めます。ナビゲーションが完了すると、currentDestination には、アプリが移動したペイン(そのペインに表示されたコンテンツを含む)が含まれます。content プロパティは、元の remember 呼び出しで指定された型(この例では MyItem)と同じ型であるため、表示する必要があるすべてのデータのプロパティにアクセスできます。

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane =
        // ...
        detailPane = {
            AnimatedPane {
                navigator.currentDestination?.content?.let {
                    MyDetails(it)
                }
            }
        },
    )

上記の手順を実装すると、コードは次のようになります。

val navigator = rememberListDetailPaneScaffoldNavigator<MyItem>()

BackHandler(navigator.canNavigateBack()) {
    navigator.navigateBack()
}

ListDetailPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    listPane = {
        AnimatedPane {
            MyList(
                onItemClick = { item ->
                    // Navigate to the detail pane with the passed item
                    navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, item)
                },
            )
        }
    },
    detailPane = {
        AnimatedPane {
            // Show the detail pane content if selected item is available
            navigator.currentDestination?.content?.let {
                MyDetails(it)
            }
        }
    },
)