Modyfikowanie scen za pomocą dekoratorów scen

Dekoratory scen umożliwiają modyfikowanie sceny obliczonej przez strategię sceny aplikacji. Są one używane w drugiej fazie tworzenia treści wyświetlanych przez NavDisplay.

Takie podejście pozwala Ci enkapsulować konkretne funkcje, takie jak wyświetlanie wspólnych komponentów interfejsu, w poszczególnych dekoratorach scen.

Weźmy na przykład aplikację zwiększającą produktywność, która ma 3 trasy najwyższego poziomu: skrzynkę odbiorczą e-maili, skrzynkę odbiorczą czatów i widok kalendarza. Taka aplikacja może używać 2 dekoratorów sceny: jednego do dodawania górnego paska aplikacji, który wyświetla informacje i elementy sterujące dla bieżącej trasy najwyższego poziomu, a drugiego do dodawania trwałego paska nawigacyjnego lub panelu nawigacyjnego do przełączania się między trasami.

Tworzenie strategii dekorowania sceny

Dekoratory scen mają podobną strukturę jak strategie scen. Aby zdefiniować dekorator sceny, zaimplementuj interfejs SceneDecoratorStrategy. Ten interfejs ma metodę decorateScene, która jest analogiczna do metody calculateScene interfejsu SceneStrategy. decorateScene określa, czy może ozdobić scenę:

  • Jeśli strategia dekorowania sceny nie powinna dekorować sceny wejściowej, zwraca ją w niezmienionej postaci.
  • Jeśli powinna ozdobić scenę wejściową, zwraca nowy element Scene. Zwrócona scena przyjmuje scenę wejściową jako parametr i wywołuje metodę content sceny wejściowej w ramach własnej metody content.

Aby określić, czy i jak udekorować scenę wejściową, strategia dekorowania sceny może uwzględniać metadane zarówno wejściowego Scene, jak i zawartych w nim wpisów.

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

}

class MyDecoratingScene<T : Any>(scene: Scene<T>) : Scene<T> {

    // ...

    override val content = @Composable {
        scene.content()
    }
}

Korzystanie ze strategii dekorowania scen

Aby używać strategii dekoratora scen, podaj je w parametrze NavDisplaysceneDecoratorStrategies. Podczas dekorowania scen NavDisplay wywołuje kolejno metodę decorateScene każdej strategii, przekazując wynik każdego wywołania jako dane wejściowe do następnego.

NavDisplay(
    // ...
    sceneDecoratorStrategies = listOf(firstSceneDecoratorStrategy, secondSceneDecoratorStrategy)
)

Typowe wzorce dekoratorów scen

Podczas wdrażania dekoratorów scen warto pamiętać o tych typowych wzorcach:

Kopiowanie właściwości

W wielu przypadkach scena zwrócona po udekorowaniu powinna zawierać te same wpisy i mieć te same poprzednie wpisy co scena, którą dekoruje. Powinien on też prawdopodobnie dziedziczyć (lub modyfikować) metadane sceny, którą dekoruje, zamiast korzystać z domyślnego działania. Poniższy kod pokazuje, jak to zrobić:

class CopyingScene<T : Any>(scene: Scene<T>) : Scene<T> {
    override val entries = scene.entries
    override val previousEntries = scene.previousEntries
    override val metadata = scene.metadata

    // ...
}

Zachowaj animacje

Zgodnie z opisem w artykule Animowanie przejść między miejscami docelowymi NavDisplay automatycznie animuje przejścia między scenami, gdy zmienia się klucz pochodzący z klasy bieżącej sceny i jej właściwości key.

Po wprowadzeniu do aplikacji dekoratorów scen klasa sceny zwracana po dekoracji sceny może pozostać taka sama, nawet jeśli zmieni się klasa sceny zwracana podczas obliczania sceny. Gdy tak się stanie, a sceny dekoracyjne bezpośrednio skopiują key sceny, którą dekorują, wbudowane animacje przestaną działać, ponieważ klucz pochodny nie ulega zmianie.

Aby zachować wbudowaną obsługę animacji, dekorowanie scen powinno wykorzystywać klucz pochodzący z klasy i key sceny zwracany przez calculateScene.

class DerivedKeyScene<T : Any>(scene: Scene<T>) : Scene<T> {
    override val key = scene::class to scene.key

    // ...
}