Utwórz układ panelu pomocniczego

Układ panelu pomocniczego pozwala użytkownikowi skupić się na głównej treści aplikacji, jednocześnie wyświetlając istotne informacje pomocnicze. Na przykład w panelu głównym mogą wyświetlać się szczegóły filmu, a w panelu pomocniczym – podobne filmy, filmy tego samego reżysera lub dzieła z udziałem tych samych aktorów.

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

Implementowanie panelu pomocniczego za pomocą klasy bazowej NavigableSupportingPaneScaffold

NavigableSupportingPaneScaffold to komponent, który upraszcza implementowanie układu panelu pomocniczego w Jetpack Compose. Opisuje ona klasę SupportingPaneScaffold i dodaje wbudowaną nawigację oraz obsługę przewidywania wstecz.

Szkielet panelu pomocniczego obsługuje maksymalnie 3 panele:

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

Szablon dostosowuje się do rozmiaru okna:

  • W dużych oknach panel główny i panel pomocniczy są wyświetlane obok siebie.
  • W małych oknach widoczna jest tylko jedna karta, która zmienia się w miarę poruszania się użytkownika.

    Główne treści zajmują większość ekranu, a treści dodatkowe są umieszczone obok.
    Rysunek 1. Obsługa układu panelu.

Dodawanie zależności

NavigableSupportingPaneScaffold jest częścią biblioteki Material 3 z dopasowywaniem układu.

Dodaj do pliku build.gradle aplikacji lub modułu te 3 powiązane 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'
  • adaptive: elementy składowe niskiego poziomu, takie jak HingeInfo i Posture;
  • adaptive-layout: układy adaptacyjne, takie jak ListDetailPaneScaffoldSupportingPaneScaffold
  • adaptive-navigation: komponenty do nawigacji w ramkach i między nimi, a także elastyczne układy, które domyślnie obsługują nawigację, takie jak NavigableListDetailPaneScaffoldNavigableSupportingPaneScaffold.

Upewnij się, że projekt zawiera pakiet 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 włączyć tę funkcję, dodaj tag android:enableOnBackInvokedCallback="true" do tagu <application> [tag] lub tag android:enableOnBackInvokedCallback="true" do tagu <application> lub do tagów <activity> w pliku AndroidManifest.xml.

Gdy Twoja aplikacja jest kierowana na Androida 16 (poziom interfejsu API 36) lub nowszego, przewidywane cofnięcie jest domyślnie włączone.

Tworzenie nawigatora

W małych oknach wyświetlana jest tylko jedna karta, więc do przełączania się między nimi używaj przycisku ThreePaneScaffoldNavigator. Utwórz instancję nawigatora za pomocą funkcji rememberSupportingPaneScaffoldNavigator.

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

Przekazywanie nawigatora do szablonu

Szablon wymaga interfejsu ThreePaneScaffoldNavigator, który reprezentuje stan szablonu, ThreePaneScaffoldValuePaneScaffoldDirective.

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

Panel główny i panel pomocniczy to elementy składowe zawierające Twoje treści. Użyj opcji AnimatedPane, aby zastosować domyślne animacje paneli podczas nawigacji. Użyj wartości szablonu, 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 szablonu:

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

Elementy kompozytowe w panelu Extract

Wyodrębnij poszczególne panele SupportingPaneScaffold w osobne komponenty, aby można było ich używać wielokrotnie 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 w elementy składane upraszcza korzystanie z SupportingPaneScaffold (porównaj to z pełną implementacją szablonu 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 potrzebujesz większej kontroli nad określonymi aspektami szablonu, użyj SupportingPaneScaffold zamiast NavigableSupportingPaneScaffold. Ta funkcja akceptuje PaneScaffoldDirective i ThreePaneScaffoldValue lub ThreePaneScaffoldState osobno. Ta elastyczność umożliwia implementowanie niestandardowej logiki dla odstępów między panelami i określanie, ile paneli ma być wyświetlanych jednocześnie. Możesz też włączyć funkcję przewidywania cofnięcia, dodając ThreePaneScaffoldPredictiveBackHandler.

Dodaj ThreePaneScaffoldPredictiveBackHandler

Dołącz moduł obsługi przewidywanego przejścia wstecz, który przyjmuje instancję szablonu nawigatora, i określ backBehavior. Określa sposób, w jaki miejsca docelowe są usuwane z backstacka podczas nawigacji wstecz. Następnie prześlij 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 opcji AnimatedPane, aby ustawić domyślne animacje paneli.

Po wykonaniu tych czynności kod powinien wyglądać mniej więcej tak:

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