Utwórz układ panelu pomocniczego

Układ panelu pomocniczego pozwala użytkownikowi skupić się na głównej zawartości aplikacji, a jednocześnie wyświetlać istotne informacje pomocnicze. Na przykład w głównym panelu mogą być wyświetlane szczegóły filmu, a w panelu pomocniczym – podobne filmy, filmy tego samego reżysera lub filmy z tymi samymi aktorami.

Więcej informacji znajdziesz w wytycznych dotyczących panelu pomocniczego Material 3.

Implementowanie panelu pomocniczego z użyciem szkieletu

NavigableSupportingPaneScaffold to komponent, który upraszcza implementację układu panelu pomocniczego w Jetpack Compose. Zawiera SupportingPaneScaffold oraz wbudowaną nawigację i predykcyjne obsługiwanie gestu wstecz.

Struktura panelu pomocniczego może zawierać maksymalnie 3 panele:

  • Główny panel: wyświetla główną zawartość.
  • Panel pomocniczy: zawiera dodatkowy kontekst lub narzędzia związane z panelem głównym.
  • Dodatkowy panel (opcjonalny): używany w razie potrzeby do wyświetlania dodatkowych treści.

Szkielet dostosowuje się do rozmiaru okna:

  • W przypadku dużych okien panele główny i pomocniczy wyświetlają się obok siebie.
  • W małych oknach widoczny jest tylko jeden panel naraz, który zmienia się w miarę poruszania się użytkowników.

    Główne treści zajmujące większość ekranu, a treści pomocnicze znajdujące się obok.
    Rysunek 1. Układ panelu pomocniczego.

Dodawanie zależności

NavigableSupportingPaneScaffold jest częścią biblioteki układów adaptacyjnych Material 3.

Dodaj do pliku build.gradle aplikacji lub modułu te 3 zależności:

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'
  • adaptacyjne: elementy składowe niskiego poziomu, takie jak HingeInfoPosture;

  • adaptive-layout: układy adaptacyjne, np. ListDetailPaneScaffold i SupportingPaneScaffold

  • adaptive-navigation: komponenty kompozycyjne do nawigacji w panelach i między nimi, a także układy adaptacyjne, które domyślnie obsługują nawigację, np. NavigableListDetailPaneScaffoldNavigableSupportingPaneScaffold.

Upewnij się, że projekt zawiera bibliotekę compose-material3-adaptive w wersji 1.1.0-beta1 lub nowszej.

Włączanie gestu przewidywanego przejścia wstecz

Aby włączyć animacje przewidywanego przejścia wstecz w Androidzie 15 lub starszym, musisz wyrazić zgodę na obsługę gestu przewidywanego przejścia wstecz. Aby wyrazić zgodę, dodaj android:enableOnBackInvokedCallback="true" do tagu <application> lub do poszczególnych tagów <activity> w pliku AndroidManifest.xml.

Gdy aplikacja jest kierowana na Androida 16 (API na poziomie 36) lub nowszego, funkcja przewidywanego powrotu jest domyślnie włączona.

Tworzenie nawigatora

W małych oknach wyświetlany jest tylko jeden panel naraz, więc do przełączania się między panelami używaj ThreePaneScaffoldNavigator. Utwórz instancję nawigatora za pomocą funkcji rememberSupportingPaneScaffoldNavigator.

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

Przekaż nawigator do elementu Scaffold

Szkielet wymaga ThreePaneScaffoldNavigator, czyli interfejsu reprezentującego stan szkieletu, ThreePaneScaffoldValuePaneScaffoldDirective.

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = { /*...*/ },
    supportingPane = { /*...*/ },
)

Główny i pomocniczy panel to komponenty zawierające Twoje treści. Użyj AnimatedPane, aby podczas nawigacji zastosować domyślne animacje panelu. Użyj wartości struktury, aby sprawdzić, czy panel pomocniczy jest ukryty. Jeśli tak, wyświetl przycisk, który wywołuje funkcję navigateTo(SupportingPaneScaffoldRole.Supporting), aby wyświetlić panel pomocniczy.

Oto pełna implementacja szkieletu:

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        AnimatedPane(
            modifier = Modifier
                .safeContentPadding()
                .background(Color.Red)
        ) {
            if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) {
                Button(
                    modifier = Modifier
                        .wrapContentSize(),
                    onClick = {
                        scope.launch {
                            scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Supporting)
                        }
                    }
                ) {
                    Text("Show supporting pane")
                }
            } else {
                Text("Supporting pane is shown")
            }
        }
    },
    supportingPane = {
        AnimatedPane(modifier = Modifier.safeContentPadding()) {
            Text("Supporting pane")
        }
    }
)

Wyodrębnianie funkcji kompozycyjnych z panelu

Wyodrębnij poszczególne panele SupportingPaneScaffold do osobnych komponentów kompozycyjnych, aby można było ich używać ponownie i testować. Aby uzyskać dostęp do AnimatedPane, użyj ThreePaneScaffoldScope, jeśli chcesz używać domyślnych animacji:

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.MainPane(
    shouldShowSupportingPaneButton: Boolean,
    onNavigateToSupportingPane: () -> Unit,
    modifier: Modifier = Modifier,
) {
    AnimatedPane(
        modifier = modifier.safeContentPadding()
    ) {
        // Main pane content
        if (shouldShowSupportingPaneButton) {
            Button(onClick = onNavigateToSupportingPane) {
                Text("Show supporting pane")
            }
        } else {
            Text("Supporting pane is shown")
        }
    }
}

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.SupportingPane(
    modifier: Modifier = Modifier,
) {
    AnimatedPane(modifier = modifier.safeContentPadding()) {
        // Supporting pane content
        Text("This is the supporting pane")
    }
}

Wyodrębnienie paneli do funkcji kompozycyjnych upraszcza korzystanie z elementu SupportingPaneScaffold (porównaj to z pełną implementacją elementu Scaffold w poprzedniej sekcji):

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane() },
)

Jeśli chcesz mieć większą kontrolę nad konkretnymi aspektami szkieletu, użyj SupportingPaneScaffold zamiast NavigableSupportingPaneScaffold. Ta funkcja akceptuje oddzielnie wartości PaneScaffoldDirectiveThreePaneScaffoldValue lub ThreePaneScaffoldState. Ta elastyczność pozwala wdrożyć niestandardową logikę odstępów między panelami i określić, ile paneli ma być wyświetlanych jednocześnie. Możesz też włączyć obsługę przewidywanego powrotu, dodając ThreePaneScaffoldPredictiveBackHandler.

Dodaj ThreePaneScaffoldPredictiveBackHandler

Dołącz moduł obsługi przewidywanego przejścia wstecz, który przyjmuje instancję nawigatora szkieletowego i określa backBehavior. Określa, jak miejsca docelowe są usuwane ze stosu wstecznego podczas nawigacji wstecznej. Następnie przesuń scaffoldDirectivescaffoldState do SupportingPaneScaffold. Użyj przeciążenia, które akceptuje ThreePaneScaffoldState, przekazując scaffoldNavigator.scaffoldState.

Określ główne i pomocnicze panele w SupportingPaneScaffold. Użyj AnimatedPane w przypadku domyślnych animacji paneli.

Po wykonaniu tych czynności kod powinien wyglądać podobnie do tego:

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

ThreePaneScaffoldPredictiveBackHandler(
    navigator = scaffoldNavigator,
    backBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange
)

SupportingPaneScaffold(
    directive = scaffoldNavigator.scaffoldDirective,
    scaffoldState = scaffoldNavigator.scaffoldState,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane() },
)