Декораторы сцен позволяют изменять сцену, рассчитанную с помощью стратегии сцен приложения. По сути, они используются на втором этапе создания контента, который показывается в NavDisplay.
Такой подход позволяет инкапсулировать определенные функции, например отображение стандартных компонентов интерфейса, в отдельные декораторы сцен.
Например, в приложении для эффективной работы может быть три маршрута верхнего уровня: папка входящих писем, папка входящих прямых переписок и календарь. В таком приложении можно использовать два декоратора сцены: один для добавления верхней панели приложения, на которой отображается информация и элементы управления для текущего маршрута верхнего уровня, а другой – для добавления постоянной панели навигации или боковой панели для перехода между маршрутами.
Как создать стратегию декоратора сцен
Декораторы сцен работают по тому же принципу, что и стратегии сцен. Чтобы определить декоратор сцены, реализуйте интерфейс SceneDecoratorStrategy. У этого интерфейса есть метод decorateScene, аналогичный методу calculateScene интерфейса SceneStrategy.
decorateScene определяет, можно ли украсить сцену:
- Если стратегия декоратора сцены не должна декорировать входную сцену, она возвращает ее без изменений.
- Если он должен украсить входную сцену, он возвращает новый
Scene. Как правило, возвращаемая сцена принимает входную сцену в качестве параметра и вызывает методcontentвходной сцены в собственном методеcontent.
Чтобы определить, нужно ли украшать входную сцену и как это сделать, стратегия декоратора сцены может учитывать метаданные как входного Scene, так и записей, содержащихся в этой сцене.
class MySceneDecoratorStrategy<T : Any> : SceneDecoratorStrategy<T> { override fun SceneDecoratorStrategyScope<T>.decorateScene(scene: Scene<T>): Scene<T> { // `shouldDecorate` determines if the scene should be decorated based on scene.metadata, // scene.entries.metadata, or any other relevant state. return if (shouldDecorate(scene)) { MyDecoratingScene(scene) } else { scene } } } data class MyDecoratingScene<T : Any>( val scene: Scene<T> ) : Scene<T> { // ... override val content = @Composable { scene.content() } }
Как использовать стратегии декоратора сцен
Чтобы использовать стратегии декоратора сцен, передайте их в NavDisplay с помощью параметра sceneDecoratorStrategies. При оформлении сцен метод NavDisplay вызывает метод decorateScene каждой стратегии по очереди, передавая выходные данные каждого вызова в качестве входных данных для следующего.
NavDisplay( // ... sceneDecoratorStrategies = listOf(firstSceneDecoratorStrategy, secondSceneDecoratorStrategy) )
Распространенные шаблоны для декораторов сцен
При реализации декораторов сцены следует учитывать следующие распространенные шаблоны:
Как копировать ресурсы
Во многих случаях сцена, полученная в результате декорирования, должна содержать те же записи и иметь те же предыдущие записи, что и декорируемая сцена. Кроме того, он должен наследовать (или изменять) метаданные сцены, которую он украшает, а не использовать поведение по умолчанию. Ниже приведен пример кода, в котором показано, как это сделать:
data class CopyingScene<T : Any>( val scene: Scene<T> ) : Scene<T> { override val entries = scene.entries override val previousEntries = scene.previousEntries override val metadata = scene.metadata // ... }
Сохранение анимации
Как описано в разделе Анимация переходов между экранами, NavDisplay автоматически анимирует переходы между сценами, когда меняется ключ, полученный из класса текущей сцены и ее свойства key.
При добавлении декораторов сцен в приложение класс сцены, возвращаемый после декорирования, может оставаться прежним, даже если класс сцены, возвращаемый во время расчета сцены, меняется. Когда это происходит и сцены, которые нужно украсить, напрямую копируют key сцены, которую они украшают, встроенные анимации больше не выполняются, потому что производный ключ не меняется.
Чтобы сохранить встроенную поддержку анимации, при оформлении сцен следует использовать ключ, полученный из класса и key сцены, возвращенных методом calculateScene.
data class DerivedKeyScene<T : Any>( val scene: Scene<T> ) : Scene<T> { override val key = scene::class to scene.key // ... }