Создайте подробный макет списка

List-detail — это шаблон пользовательского интерфейса, состоящий из двухпанельной компоновки, где одна панель отображает список элементов, а другая — подробную информацию об элементах, выбранных из списка.

Этот шаблон особенно полезен для приложений, предоставляющих подробную информацию об элементах больших коллекций, например, для почтового клиента, хранящего список адресов электронной почты и подробное содержание каждого сообщения. List-detail также можно использовать для менее критичных задач, например, для разделения настроек приложения на список категорий с отображением настроек для каждой категории в области подробностей.

Панель сведений, отображаемая рядом со страницей списка.
Рисунок 1. При достаточном размере экрана панель сведений отображается рядом с панелью списка.
После выбора элемента панель сведений занимает весь экран.
Рисунок 2. Если размер экрана ограничен, панель сведений (после выбора элемента) занимает все пространство.

Реализуйте шаблон «Список-Подробности» с помощью NavigableListDetailPaneScaffold

NavigableListDetailPaneScaffold — это компонуемый объект, упрощающий реализацию макета со списком и деталями в Jetpack Compose. Он является оболочкой ListDetailPaneScaffold и добавляет встроенную навигацию и предиктивную анимацию возврата.

Шаблон списка-детали поддерживает до трех панелей:

  1. Панель списка : отображает коллекцию элементов.
  2. Панель сведений : отображает сведения о выбранном элементе.
  3. Дополнительная панель ( необязательно ) : при необходимости предоставляет дополнительный контекст.

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

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

Объявить зависимости

NavigableListDetailPaneScaffold является частью библиотеки адаптивной навигации 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
  • адаптивная навигация: компонуемые элементы для навигации внутри панелей и между ними, а также адаптивные макеты, которые поддерживают навигацию по умолчанию, такие как NavigableListDetailPaneScaffold и NavigableSupportingPaneScaffold

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

Включите функцию прогнозируемого жеста «назад»

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

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

Базовое использование

Реализуйте NavigableListDetailPaneScaffold следующим образом:

  1. Используйте класс, представляющий выбранный контент. Используйте класс Parcelable для поддержки сохранения и восстановления выбранного элемента списка. Используйте плагин kotlin-parcelize для генерации кода.
  2. Создайте ThreePaneScaffoldNavigator с rememberListDetailPaneScaffoldNavigator .

Этот навигатор используется для перемещения между панелями списка, подробностей и дополнительных элементов. Объявляя универсальный тип, навигатор также отслеживает состояние скаффолда (то есть, какой MyItem отображается). Поскольку этот тип является парцелляционным, навигатор может сохранять и восстанавливать состояние для автоматической обработки изменений конфигурации.

  1. Передайте навигатор в составной элемент NavigableListDetailPaneScaffold .

  2. Предоставьте реализацию панели списка в NavigableListDetailPaneScaffold . Используйте AnimatedPane для применения анимации панели по умолчанию во время навигации. Затем используйте ThreePaneScaffoldNavigator для перехода к панели сведений ListDetailPaneScaffoldRole.Detail и отображения переданного элемента.

  3. Включите реализацию вашей панели сведений в NavigableListDetailPaneScaffold .

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

  1. При желании можно изменить defaultBackBehavior в NavigableListDetailPaneScaffold . По умолчанию NavigableListDetailPaneScaffold использует PopUntilScaffoldValueChange для defaultBackBehavior .

Если вашему приложению требуется другой шаблон обратной навигации, вы можете переопределить это поведение, указав другой параметр BackNavigationBehavior .

BackNavigationBehavior поведения

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

Это поведение фокусируется на изменениях общей структуры макета. В многопанельной конфигурации изменение содержимого письма в подробной панели не влияет на базовую структуру макета. Поэтому нажатие кнопки «Назад» может привести к выходу из приложения или текущего навигационного графика, поскольку в текущем контексте нет изменений макета, к которым можно вернуться. В однопанельной конфигурации нажатие кнопки «Назад» пропустит изменения содержимого в подробном представлении и вернёт в список, поскольку это представляет собой явное изменение макета.

Рассмотрим следующие примеры:

  • Многопанельное отображение: вы просматриваете электронное письмо (Элемент 1) на панели сведений. Нажатие на другое электронное письмо (Элемент 2) обновляет панель сведений, но список и панели сведений остаются видимыми. Нажатие кнопки «Назад» может привести к выходу из приложения или из текущего навигационного потока.
  • Однопанельная: вы просматриваете элемент 1, затем элемент 2, нажатие кнопки «Назад» вернет вас непосредственно на панель списка адресов электронной почты.

Используйте этот метод, когда вы хотите, чтобы пользователи воспринимали отдельные переходы макета при каждом возврате.

Изменение значения навигации.
PopUntilContentChange

Такое поведение отдаёт приоритет отображаемому контенту. Если вы просматриваете элемент 1, а затем элемент 2, нажатие кнопки «Назад» вернёт вас к элементу 1, независимо от макета.

Рассмотрим следующие примеры:

  • Многопанельное отображение: сначала вы видите элемент 1 на панели сведений, затем нажимаете элемент 2 в списке. Панель сведений обновляется. Нажатие кнопки «Назад» вернет панель сведений к элементу 1.
  • Однопанельный режим: происходит тот же возврат содержимого.

Используйте этот вариант, когда пользователь ожидает вернуться к ранее просмотренному контенту с помощью действия «Назад».

переход между двумя панелями подробностей
PopUntilCurrentDestinationChange

Это поведение выдвигает стек переходов назад до тех пор, пока не изменится текущее назначение навигации . Это применимо как к однопанельным, так и к многопанельным макетам.

Рассмотрим следующие примеры:

Независимо от того, используете ли вы макет с одной или несколькими панелями, нажатие кнопки «Назад» всегда перемещает фокус с выделенного элемента навигации на предыдущую позицию. В нашем почтовом приложении это означает, что визуальное обозначение выбранной панели сместится.

Используйте этот метод, когда для удобства пользователя критически важно поддерживать четкую визуальную индикацию текущей навигации.

навигация между панелями подробностей и списков
PopLatest

Эта опция удаляет из стека переходов только последний пункт назначения. Используйте эту опцию для навигации назад без пропуска промежуточных состояний.

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

val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator<MyItem>()
val scope = rememberCoroutineScope()

NavigableListDetailPaneScaffold(
    navigator = scaffoldNavigator,
    listPane = {
        AnimatedPane {
            MyList(
                onItemClick = { item ->
                    // Navigate to the detail pane with the passed item
                    scope.launch {
                        scaffoldNavigator.navigateTo(
                            ListDetailPaneScaffoldRole.Detail,
                            item
                        )
                    }
                },
            )
        }
    },
    detailPane = {
        AnimatedPane {
            // Show the detail pane content if selected item is available
            scaffoldNavigator.currentDestination?.contentKey?.let {
                MyDetails(it)
            }
        }
    },
)