Создание вспомогательного макета панели

Вспомогательная панель позволяет пользователю сосредоточиться на основном содержимом приложения, одновременно отображая необходимую дополнительную информацию. Например, в основной панели может отображаться информация о фильме, а во вспомогательной — список похожих фильмов, фильмов того же режиссера или работ с участием тех же актеров.

Более подробную информацию см. в руководстве по использованию вспомогательной панели «Материал 3» .

Реализуйте вспомогательную панель с помощью каркаса.

NavigableSupportingPaneScaffold — это компонент, упрощающий реализацию макета вспомогательных панелей в Jetpack Compose. Он является оберткой SupportingPaneScaffold и добавляет встроенную навигацию и предиктивную обработку возврата.

Опорный каркас для стеклянных панелей поддерживает до трех стекол:

  • Главная панель : Отображает основное содержимое.
  • Вспомогательная панель : Предоставляет дополнительный контекст или инструменты, связанные с основной панелью.
  • Дополнительная панель (необязательно) : используется для размещения дополнительного контента при необходимости.

Конструкция адаптируется в зависимости от размера окна:

  • В больших окнах основное и вспомогательные стекла расположены рядом друг с другом.
  • В небольших окнах одновременно видна только одна панель, переключение между которыми происходит по мере перемещения пользователя.

    Основной контент занимает большую часть экрана, а вспомогательный контент располагается рядом.
    Рисунок 1. Схема расположения вспомогательных панелей.

Добавить зависимости

NavigableSupportingPaneScaffold является частью библиотеки адаптивной компоновки Material 3 .

Добавьте следующие три связанные зависимости в файл build.gradle вашего приложения или модуля:

Котлин

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

Классный

implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
  • адаптивный : низкоуровневые компоненты, такие как HingeInfo и Posture

  • adaptive-layout : Адаптивные макеты, такие как ListDetailPaneScaffold и SupportingPaneScaffold

  • adaptive-navigation : Компоненты для навигации внутри и между панелями, а также адаптивные макеты, поддерживающие навигацию по умолчанию, такие как NavigableListDetailPaneScaffold и NavigableSupportingPaneScaffold

Убедитесь, что ваш проект включает compose-material3-adaptive версии 1.1.0-beta1 или выше.

Включите функцию автоматического ввода жеста "назад"

Чтобы включить анимацию «Назад» с функцией прогнозирования в Android 15 или более ранних версиях, необходимо включить поддержку жеста «Назад». Для этого добавьте android:enableOnBackInvokedCallback="true" в тег ` <application> или отдельные теги <activity> ` в файле AndroidManifest.xml .

Если ваше приложение ориентировано на Android 16 (уровень API 36) или выше, функция предиктивного ответа "Назад" включается по умолчанию.

Создать навигатор

В небольших окнах одновременно отображается только одна панель, поэтому используйте ThreePaneScaffoldNavigator для перемещения между панелями. Создайте экземпляр навигатора с помощью rememberSupportingPaneScaffoldNavigator .

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

Передайте штурмана на эшафот.

Для работы с генератором кода требуется интерфейс ThreePaneScaffoldNavigator , представляющий состояние генератора кода, ThreePaneScaffoldValue и PaneScaffoldDirective .

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

Основная панель и вспомогательная панель представляют собой составные элементы, содержащие ваш контент. Используйте AnimatedPane для применения анимации панелей по умолчанию во время навигации. Используйте значение scaffold, чтобы проверить, скрыта ли вспомогательная панель; если да, отобразите кнопку, которая вызывает navigateTo(SupportingPaneScaffoldRole.Supporting) для отображения вспомогательной панели.

Для больших экранов используйте метод ThreePaneScaffoldNavigator.navigateBack() для закрытия вспомогательной панели, передав константу BackNavigationBehavior.PopUntilScaffoldValueChange . Вызов этого метода принудительно перестраивает NavigableSupportingPaneScaffold . Во время перестройки проверьте свойство ThreePaneScaffoldNavigator.currentDestination , чтобы определить, следует ли отображать вспомогательную панель.

Вот полная реализация шаблона:

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

        }
    }
)

Извлечь составные элементы панели

Выделите отдельные панели из SupportingPaneScaffold в отдельные компонуемые объекты, чтобы сделать их многоразовыми и пригодными для тестирования. Используйте ThreePaneScaffoldScope для доступа к 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")
        }

    }
}

Выделение панелей в составные элементы упрощает использование SupportingPaneScaffold (сравните приведенный ниже пример с полной реализацией этого элемента в предыдущем разделе):

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

Если вам требуется больший контроль над определенными аспектами структуры проекта, рассмотрите возможность использования SupportingPaneScaffold вместо NavigableSupportingPaneScaffold . Этот метод принимает отдельно PaneScaffoldDirective и ThreePaneScaffoldValue или ThreePaneScaffoldState . Такая гибкость позволяет реализовать собственную логику для интервалов между панелями и определить, сколько панелей должно отображаться одновременно. Вы также можете включить поддержку предиктивной отправки, добавив ThreePaneScaffoldPredictiveBackHandler .

Добавить ThreePaneScaffoldPredictiveBackHandler

Прикрепите обработчик предсказания возврата, который принимает экземпляр навигатора Scaffold и указывает backBehavior . Это определяет, как пункты назначения удаляются из стека возврата во время навигации назад. Затем передайте scaffoldDirective и scaffoldState в SupportingPaneScaffold . Используйте перегрузку, которая принимает ThreePaneScaffoldState , передавая scaffoldNavigator.scaffoldState .

Определите основную и вспомогательные панели в классе SupportingPaneScaffold . Используйте AnimatedPane для анимации панелей по умолчанию.

После выполнения этих шагов ваш код должен выглядеть примерно так:

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