Układ panelu pomocniczego pozwala użytkownikowi skupić się na głównej treści aplikacji, jednocześnie wyświetlając odpowiednie informacje pomocnicze. Na przykład panel główny może wyświetlać szczegóły filmu, a panel pomocniczy – listę podobnych filmów, filmów tego samego reżysera lub dzieł z udziałem tych samych aktorów.
Więcej informacji znajdziesz w wytycznych dotyczących panelu pomocniczego Material 3.
Implementowanie panelu pomocniczego za pomocą rusztowania
NavigableSupportingPaneScaffold to funkcja kompozycyjna, która upraszcza implementację układu panelu pomocniczego w Jetpack Compose. Zawiera SupportingPaneScaffold i dodaje wbudowaną nawigację oraz obsługę przewidywanego przejścia wstecz.
Szkielet panelu pomocniczego obsługuje maksymalnie 3 panele:
- Panel główny: wyświetla główną zawartość.
- Panel pomocniczy: zawiera dodatkowy kontekst lub narzędzia związane z panelem głównym.
- Dodatkowy panel (opcjonalny): służy do wyświetlania dodatkowych treści, gdy jest to konieczne.
Szkielet dostosowuje się do rozmiaru okna:
- W dużych oknach panel główny i panel pomocniczy wyświetlają się obok siebie.
W małych oknach widoczny jest tylko jeden panel naraz, który zmienia się, gdy użytkownicy przechodzą do innych sekcji.
Rysunek 1. Układ panelu pomocniczego.
Dodawanie zależności
NavigableSupportingPaneScaffold jest częścią biblioteki układu adaptacyjnego Material 3.
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")
Dynamiczny
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
HingeInfoiPosture;adaptive-layout: układy adaptacyjne, takie jak
ListDetailPaneScaffoldiSupportingPaneScaffoldadaptive-navigation: funkcje kompozycyjne do poruszania się w obrębie paneli i między nimi, a także układy adaptacyjne, które domyślnie obsługują nawigację, takie jak
NavigableListDetailPaneScaffoldiNavigableSupportingPaneScaffold.
Sprawdź, czy Twój projekt zawiera 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 na 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 poszczególnych tagów <activity> w pliku AndroidManifest.xml.
Gdy aplikacja jest kierowana na Androida 16 (API na poziomie 36) lub nowszego, przewidywane przejście wstecz jest domyślnie włączone.
Tworzenie nawigatora
W małych oknach wyświetlany jest tylko jeden panel naraz, więc do przechodzenia między panelami używaj ThreePaneScaffoldNavigator. Utwórz instancję nawigatora za pomocą rememberSupportingPaneScaffoldNavigator.
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope()
Przekazywanie nawigatora do rusztowania
Szkielet wymaga interfejsu ThreePaneScaffoldNavigator, który reprezentuje stan szkieletu, ThreePaneScaffoldValue i PaneScaffoldDirective.
NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { /*...*/ }, supportingPane = { /*...*/ }, )
Panel główny i panel pomocniczy to elementy kompozycyjne zawierające Twoje treści. Użyj AnimatedPane, aby zastosować domyślne animacje panelu podczas nawigacji. Użyj wartości scaffold, 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.
W przypadku dużych ekranów użyj metody ThreePaneScaffoldNavigator.navigateBack(), aby zamknąć panel dodatkowy, przekazując stałą BackNavigationBehavior.PopUntilScaffoldValueChange. Wywołanie tej metody wymusza rekompozycję elementu NavigableSupportingPaneScaffold.
Podczas rekompozycji sprawdź właściwość ThreePaneScaffoldNavigator.currentDestination, aby określić, czy wyświetlać panel dodatkowy.
Oto pełna implementacja szkieletu:
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() val backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange 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()) { Column { // Allow users to dismiss the supporting pane. Use back navigation to // hide an expanded supporting pane. if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) { // Material design principles promote the usage of a right-aligned // close (X) button. IconButton( modifier = Modifier.align(Alignment.End).padding(16.dp), onClick = { scope.launch { scaffoldNavigator.navigateBack(backNavigationBehavior) } } ) { Icon(Icons.Default.Close, contentDescription = "Close") } } Text("Supporting pane") } } } )
Wyodrębnianie funkcji kompozycyjnych panelu
Wyodrębnij poszczególne panele SupportingPaneScaffold do własnych funkcji kompozycyjnych, aby można było ich używać wielokrotnie i testować. Jeśli chcesz używać domyślnych animacji, użyj ThreePaneScaffoldScope, aby uzyskać dostęp do AnimatedPane:
@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( scaffoldNavigator: ThreePaneScaffoldNavigator<Any>, modifier: Modifier = Modifier, backNavigationBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange, ) { val scope = rememberCoroutineScope() AnimatedPane(modifier = Modifier.safeContentPadding()) { Column { // Allow users to dismiss the supporting pane. Use back navigation to // hide an expanded supporting pane. if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) { // Material design principles promote the usage of a right-aligned // close (X) button. IconButton( modifier = modifier.align(Alignment.End).padding(16.dp), onClick = { scope.launch { scaffoldNavigator.navigateBack(backNavigationBehavior) } } ) { Icon(Icons.Default.Close, contentDescription = "Close") } } Text("Supporting pane") } } }
Wyodrębnienie paneli do funkcji kompozycyjnych upraszcza korzystanie z SupportingPaneScaffold (porównaj poniższy kod z pełną implementacją rusztowania 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(scaffoldNavigator = scaffoldNavigator) }, )
Jeśli potrzebujesz większej kontroli nad konkretnymi aspektami szkieletu, rozważ użycie SupportingPaneScaffold zamiast NavigableSupportingPaneScaffold. Ta metoda akceptuje oddzielnie PaneScaffoldDirective i ThreePaneScaffoldValue lub ThreePaneScaffoldState. Dzięki tej elastyczności możesz 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 przejścia wstecz, dodając ThreePaneScaffoldPredictiveBackHandler.
Dodaj ThreePaneScaffoldPredictiveBackHandler
Dołącz moduł obsługi przewidywanego przejścia wstecz, który pobiera instancję nawigatora Scaffold, i określ backBehavior. Określa, jak miejsca docelowe są usuwane ze stosu podczas przechodzenia wstecz. Następnie przekaż scaffoldDirective i scaffoldState do SupportingPaneScaffold. Użyj przeciążenia, które akceptuje ThreePaneScaffoldState, przekazując scaffoldNavigator.scaffoldState.
Zdefiniuj panel główny i panele pomocnicze w elemencie SupportingPaneScaffold. Użyj AnimatedPane, aby uzyskać domyślne animacje panelu.
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(scaffoldNavigator = scaffoldNavigator) }, )