Crea un layout elenco-dettagli

Dettaglio elenco è un pattern di UI costituito da un layout a due riquadri in cui un riquadro presenta un elenco di elementi e un altro riquadro mostra i dettagli degli elementi selezionati dall'elenco.

Il pattern è particolarmente utile per le applicazioni che forniscono informazioni approfondite sugli elementi di raccolte di grandi dimensioni, ad esempio un client di posta che contiene un elenco di email e i contenuti dettagliati di ogni messaggio. List-detail può essere utilizzato anche per percorsi meno critici, ad esempio per suddividere le preferenze dell'app in un elenco di categorie con le preferenze per ogni categoria nel riquadro dei dettagli.

Implementare il pattern dell'interfaccia utente con ListDetailPaneScaffold

ListDetailPaneScaffold è un componente composable che semplifica l'implementazione del pattern elenco-dettagli nella tua app. Uno scafo elenco-dettagli può essere composto da un massimo di tre riquadri: un riquadro elenco, un riquadro dei dettagli e un riquadro aggiuntivo facoltativo. La struttura di supporto gestisce i calcoli dello spazio sullo schermo. Se le dimensioni dello schermo sono sufficientemente grandi, il riquadro dei dettagli viene visualizzato accanto al riquadro dell'elenco. Su schermi di piccole dimensioni, la struttura passa automaticamente alla visualizzazione a schermo intero dell'elenco o del riquadro dei dettagli.

Un riquadro dei dettagli visualizzato accanto alla pagina dell'elenco.
Figura 1. Quando le dimensioni dello schermo sono sufficienti, il riquadro dei dettagli viene visualizzato accanto al riquadro dell'elenco.
Una volta selezionato un elemento, il riquadro dei dettagli occupa l'intera schermata.
Figura 2. Quando le dimensioni dello schermo sono limitate, il riquadro dei dettagli (dopo aver selezionato un elemento) occupa tutto lo spazio.

Dichiarare le dipendenze

ListDetailPaneScaffold fa parte della libreria di layout adattivo Material 3.

Aggiungi le tre dipendenze correlate seguenti al file build.gradle della tua app o del tuo modulo:

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: componenti di base di basso livello come HingeInfo e Posture
  • adaptive-layout: layout adattivi comeListDetailPaneScaffold eSupportingPaneScaffold
  • adaptive-navigation: composabili per la navigazione all'interno e tra i riquadri

Utilizzo di base

Implementa ListDetailPaneScaffold come segue:

  1. Utilizza un'entità che rappresenti i contenuti da selezionare. Questa classe deve essere Parcelable per supportare il salvataggio e il ripristino dell'elemento dell'elenco selezionato. Utilizza il plug-in kotlin-parcelize per generare il codice per te.

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

  2. Crea un ThreePaneScaffoldNavigator con rememberListDetailPaneScaffoldNavigator e aggiungi un BackHandler. Questo navigatore viene utilizzato per spostarsi tra i riquadri elenco, dei dettagli e aggiuntivi. Dichiarando un tipo generico, il navigatore monitora anche lo stato della struttura (ovvero quale MyItem viene visualizzato). Poiché questo tipo è parcellabile, lo stato può essere salvato e ripristinato dal navigatore per gestire automaticamente le modifiche alla configurazione. BackHandler supporta la navigazione a ritroso utilizzando il gesto o il pulsante Indietro di sistema. Il comportamento previsto del pulsante Indietro per un ListDetailPaneScaffold dipende dalle dimensioni della finestra e dal valore attuale della struttura. Se ListDetailPaneScaffold può supportare il ritorno allo stato corrente, canNavigateBack() è true, attivando BackHandler.

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

  3. Passa il scaffoldState dal navigator al composable ListDetailPaneScaffold.

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

  4. Fornisci l'implementazione del riquadro degli elenchi al ListDetailPaneScaffold. Utilizza AnimatedPane per applicare le animazioni dei riquadri predefinite durante la navigazione. Quindi, usa ThreePaneScaffoldNavigator per passare al riquadro dei dettagli ListDetailPaneScaffoldRole.Detail e visualizzare l'elemento passato.

    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. Includi l'implementazione del riquadro dei dettagli in ListDetailPaneScaffold. Al termine della navigazione, currentDestination contiene il riquadro a cui è passata l'app, inclusi i contenuti visualizzati nel riquadro. La proprietà content è dello stesso tipo specificato nella chiamata remember originale (MyItem in questo esempio), quindi puoi anche accedere alla proprietà per tutti i dati che devi visualizzare.

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

Dopo aver implementato i passaggi precedenti, il codice dovrebbe avere il seguente aspetto:

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)
            }
        }
    },
)