Android Arsenal – Navigation

Eine kleine und einfache, aber dennoch ausgereifte und anpassbare Navigationsbibliothek für Jetpack komponieren:

  • Voll Typsicherheit
  • Staatlicher Wiederaufbau
  • Verschachtelte Navigation mit unabhängigen Backstacks
  • Eigener Lebenszyklus, gespeicherter Zustand und Anzeigemodelle für jeden Backstack-Eintrag
  • Animierte Übergänge
  • Die Navigationslogik kann einfach in die ViewModel-Schicht verschoben werden
  • Keine Builder, keine obligatorischen Superklassen für Ihre Verbundwerkstoffe
  • Kann verwendet werden, um Dialoge zu verwalten

Wir fangen an

Fügen Sie Ihrem Projekt eine Abhängigkeit hinzu:

implementation("dev.olshevski.navigation:reimagined:1.0.0-beta01")

Definieren Sie beispielsweise eine Reihe von Bildschirmen als versiegelte Klasse:

sealed class Screen : Parcelable {

    @Parcelize
    object First : Screen()

    @Parcelize
    data class Second(val id: Int) : Screen()

    @Parcelize
    data class Third(val text: String) : Screen()

}

Erstellen Sie komponiert mit NavController ich NavHost:

@Composable
fun NavHostScreen() {
    val navController = rememberNavController<Screen>(
        startDestination = Screen.First,
    )

    NavBackHandler(navController)

    NavHost(controller = navController) { screen ->
        when (screen) {
            Screen.First -> Column {
                Text("First screen")
                Button(onClick = {
                    navController.navigate(Screen.Second(id = 42))
                }) {
                    Text("To Second screen")
                }
            }
            is Screen.Second -> Column {
                Text("Second screen: ${screen.id}")
                Button(onClick = {
                    navController.navigate(Screen.Third(text = "Hello"))
                }) {
                    Text("To Third screen")
                }
            }
            is Screen.Third -> {
                Text("Third screen: ${screen.text}")
            }
        }
    }
}

Wie du sehen kannst, NavController verwendet, um zwischen Bildschirmen zu wechseln, NavBackHandler übernimmt den Schub für den Rücken und NavHost Es bietet einfach eine Komposition, die dem neuesten Backstack-Ziel entspricht. So einfach.

Grundlagen

Hier ist der allgemeine Arbeitsablauf der Bibliothek:

Lassen Sie uns auf jeden von ihnen ins Detail gehen.

NavController

Dies ist der Hauptnavigationskontrollpunkt. Verfolgt alle aktuellen Backstack-Einträge und hält sie bei Prozessaktivitäten/Erneuerung fest.

NavController kann mit erstellt werden rememberNavController innerhalb der Komposition oder mit navController außerhalb davon. Letzteres kann verwendet werden, um den NavController im ViewModel zu speichern. Da es die Parcelable-Schnittstelle implementiert, kann (und sollte) es darin gespeichert werden SavedStateHandle.

Beide rememberNavController ich navController Methoden akzeptieren startDestination als Parameter. Wenn Sie einen NavController mit einer beliebigen Anzahl von Backstack-Elementen erstellen möchten, können Sie verwenden initialBackstack stattdessen Parameter.

Reiseziele

NavController akzeptiert alle zulässigen Typen als Ziele. Die Anforderungen sind:

  1. Der Typ muss entweder Parcelable oder Serializable oder Primitive oder ein beliebiger anderer Typ sein, der geschrieben werden kann Baugrundstück.

  2. Der Typ muss jeder sein Stabiloder Unveränderlichoder primitiv.

Navigationsmethoden

Es gibt mehrere vordefinierte Methoden, die für die grundlegende In-App-Navigation geeignet sind: navigate, pop, popUpTo, popAll, replaceLast, replaceUpTo, replaceAll. Sie sind alle ziemlich verständlich.

Wenn Ihr Anwendungsfall eine fortgeschrittene Backstack-Manipulation erfordert, können Sie es verwenden setNewBackstackEntries Methode. Tatsächlich ist dies die einzige öffentliche Methode, die in NavController definiert ist, alle anderen Methoden werden als Erweiterungen und Verwendung angegeben setNewBackstackEntries unter der Haube. Sie können sehen, wie die neue Expansionsmethode ist navigateToTab ist implementiert in Probe.

NavBackstack

Dies ist eine schreibgeschützte Klasse, mit der Sie auf aktuelle Backstack-Einträge und die neueste NavAction zugreifen können. Eigenschaften sind Backup MutableStateCompose wird also über Änderungen benachrichtigt.

Wenn Sie Backstack-Änderungen außerhalb der Komposition hören möchten, können Sie dies einstellen onBackstackChange Listener im NavController.

NavHost

NavHost ist ein Compiler, der den letzten Backstack-Eintrag anzeigt und alle Komponenten bereitstellt, die diesem bestimmten Eintrag zugeordnet sind: Lebenszyklus, SavedStateRegistry ich ViewModelStore. Alle diese Komponenten werden durch bereitgestellt CompositionLocalProvider innerhalb der entsprechenden Eigentümer LocalLifecycleOwner, LocalSavedStateRegistryOwner ich LocalViewModelStoreOwner.

Komponenten werden gespeichert, bis der zugeordnete Eintrag aus dem Backstack entfernt wird (oder bis der übergeordnete Eintrag, der den aktuellen Slave-NavHost enthält, entfernt wird).

NavHost selbst bietet keine animierten Übergänge, es springt einfach zum nächsten Ziel.

AnimierterNavHost

AnimatedNavHost enthält alle Funktionen eines regulären NavHost, unterstützt aber auch animierte Übergänge. Der Standardübergang ist ein einfaches Crossfade, aber Sie können jeden Übergang im Detail an Ihren eigenen anpassen AnimatedNavHostTransitionSpec Implementierung.

Hier ist eine mögliche Implementierung von AnimatedNavHostTranzitionSpec:

val CustomTransitionSpec = AnimatedNavHostTransitionSpec<Any?> { action, from, to ->
    val direction = if (action == NavAction.Pop) {
        AnimatedContentScope.SlideDirection.End
    } else {
        AnimatedContentScope.SlideDirection.Start
    }
    slideIntoContainer(direction) with slideOutOfContainer(direction)
}

Stellen Sie es auf AnimatedNavHost ein:

AnimatedNavHost(
    controller = navController,
    transitionSpec = CustomTransitionSpec
) { destination ->
    // ...
}

und am Ende sieht es so aus:

In AnimatedNavHostTransitionSpec erhalten Sie die Parameter:

  • action – ein Hinweis auf die neueste NavController-Methode, die den Backstack geändert hat
  • from – vorheriges sichtbares Ziel
  • to – Ziel sichtbares Ziel

Diese Informationen reichen aus, um Übergänge für jede mögliche Kombination von Bildschirm- und Navigationsaktionen auszuwählen.

NavAction

Es gibt vier Standardtypen von NavAction:

  • Pop, Replace ich Navigate – passende Einrichtungen pop…, replace…, navigate NavController-Methoden
  • Idle – die Standardaktion des neu erstellten NavControllers

Sie können auch einen neuen Aktionstyp erstellen, indem Sie die Zusammenfassung erweitern NavAction Klasse. Übertragen Sie diese neue Art auf setNewBackstackEntries NavController-Methode und verarbeiten Sie sie in AnimatedNavHostTransitionSpec.

Die letzte Aktion ist auch über erreichbar action im Besitz von NavBackstack.

DialogNavHost

Eine Version von NavHost, die besser für die Anzeige von Dialogen geeignet ist. Es basiert auf AnimatedNavHost und sorgt für einen fließenderen Übergang zwischen flimmerfreien Scrim-/Fade-Dialogen.

Wenn Sie sehen möchten, wie Sie die Navigation durch Dialoge implementieren können, erkunden Sie Probe.

Beachten Sie, dass DialogNavHost Ihre Komponenten nicht in Dialog umschließt. Sie müssen entweder Dialog oder AlertDialog verwenden, die innerhalb einer kompiliert werden können contentSelector ich selbst.

Handhabung im Hintergrund

Back-Handling in der Bibliothek ist Opt-in, nicht Opt-out. Weder NavController noch NavHost steuern allein das Drücken einer Zurück-Taste. Du kannst hinzufügen NavBackHandler oder gemein BackHandler um auf Schub nach hinten zu reagieren, wo es nötig ist.

NavBackHandler ist die grundlegendste Implementierung von Calling BackHandler pop bis ein Gegenstand im Stapel verbleibt. Es wird dann deaktiviert, sodass jeder übergeordnete BackHandler auf das Drücken der Zurück-Schaltfläche reagieren kann.

Wichtiger Hinweis: Stellen Sie immer Ihren NavBackHandler / BackHandler ein Vor passender NavHost. Lesen Sie die Erklärung Hier.

Verschachtelte Navigation

Das Hinzufügen einer verschachtelten Navigation ist so einfach wie das Platzieren eines NavHosts in einem anderen. Alles ist richtig und einfach gemacht.

Sie können so viele Schichten so tief gehen, wie Sie möchten. Es ist wie Fraktaleaber im Navi.

Wiederherstellen von Werten zu früheren Zielen

Da Zieltypen nicht unbedingt unveränderlich sein müssen, können Sie sie beim Backstacking ändern. Dies kann verwendet werden, um Werte von anderen Zielen wiederherzustellen. Machen Sie einfach eine veränderbare Eigenschaft mit einem Backup mutableStateOf und ändern Sie es nach Bedarf. Sie können die Demo sehen Hier.

Notiz: Im Allgemeinen macht die Rückgabe von Werten an das vorherige Ziel die Logik der Navigation komplexer. Außerdem garantiert dieser Ansatz nicht die Kompilierzeittypsicherheit. Verwenden Sie es vorsichtig und wenn Sie sicher sind, was Sie tun. Manchmal kann es einfacher sein, einen gemeinsamen Eigentümer der Bedingung zu verwenden.

Dokumentation und Probe

Erkunden Sie die KDoc-Bibliotheksdokumentation für weitere Details zu jeder Komponente und allen unterstützten Funktionen.

Recherchieren Sie auch eine Probe. Es bietet Demonstrationen aller oben genannten Funktionen und noch mehr. Das Beispiel zeigt:

  • verschachtelte Navigation
  • Tab-Navigation
  • Verwenden Sie NavHost / AnimatedNavHost
  • Dialoge
  • Weiterleitung und Wiederherstellung von Werten
  • ViewModels
  • Anheben des NavControllers auf die ViewModel-Schicht

Warum Beta

Ich bin sehr zufrieden mit der Form und Form der Bibliothek. Ich verbrachte lange schlaflose Nächte damit, alle Eckgehäuse zu debuggen und zu polieren.

Im Moment freue ich mich über Ihr Feedback und eine geringfügige Feinabstimmung der API (falls vorhanden). Sollten sich Änderungen ergeben, können Sie mit einem Hinweis in den Release Notes rechnen.

Oh

Ich habe lange über die Architektur von Android-Anwendungen und insbesondere über die Navigation nachgedacht. Als ich Compose kennenlernte, konnte ich endlich eine Navigationsstruktur erstellen, die alle meine Bedürfnisse perfekt erfüllt.

Gestalten in Form einer öffentlichen Bibliothek schließt für mich die Gestalt. Ich bin endlich fertig damit. Auf neue Projekte!

Wenn Ihnen diese Bibliothek gefällt und Sie sie nützlich finden, markieren Sie das Projekt und teilen Sie es mit Ihren Mitentwicklern. Ein bisschen Werbung schadet nie.