Die Navigation ist eine der wichtigen Komponenten in Jetpack, mit der App-Seitensprünge entworfen und organisiert werden. Da die offizielle Empfehlung lautet, Framgent für die Seitenimplementierung zu verwenden, fällt Ihnen als Erstes Fragment ein, wenn Sie die Navigation erwähnen. Tatsächlich unterstützt die Navigation jedoch auch andere Arten von Seitenimplementierungen, z. B. benutzerdefinierte Ansichten. In diesem Artikel wird die Verwendung der benutzerdefinierten Ansicht in der Navigation vorgestellt.
Lassen Sie uns vor der formalen Einführung die grundlegende Verwendung der Navigation überprüfen:
Grundstruktur der Navigation
Die Verwendung der Navigation umfasst hauptsächlich die folgenden Objekte
- Graph
verwendet XML, um die Seite (das Ziel) der APP und den Sprungpfad zwischen den einzelnen Seiten zu entwerfen. Android Studio bietet einen Editor zum Bearbeiten des Graphen. - NavHost
NavHost ist ein Container, in dem alle Knoten im Diagramm gehostet werden. Die Navigation bietet eine Standardimplementierung von NavHost für FragmentNavHostFragment
. Es versteht sich, dass alle Fragmente im Diagramm ChildFragment sind. In dem heute vorgestellten benutzerdefinierten Ansichtsszenario ist auch die NavHost-Implementierung für die benutzerdefinierte Ansicht erforderlich. - Controller
Jeder NavHost verfügt über einen Controller, der den Sprung zwischen den Knoten im NavHost verwaltet - Navigator
Controller implementiert bestimmte Sprünge durch Aufrufen von Navigator, und Navigator führt bestimmte Sprungimplementierungen durch
Arbeitsprinzip
Jede Seite in der Navigation ist ein Ziel, das Fragment, Aktivität oder Ansicht sein kann. Verknüpfen Sie das Quellziel mit dem Ziel Ziel durch Aktion und springen Sie vom aktuellen Ziel zum Ziel Ziel durch Aktion.
Ähnlich wie bei der Startaktivität muss beim Starten der APP ein Startziel als Startseitenanzeige definiert werden.
Wie bereits erwähnt, verfügt NavHost über spezifische Implementierungen für verschiedene Ziele. NavController verfügt je nach Zieltyp auch über unterschiedliche Erfassungsmethoden, die jedoch alle ähnlich sind:
- Fragment.findNavController ()
- View.findNavController ()
- Activity.findNavController (viewId: Int)
Nachdem Sie den Controller erhalten haben, können Sie navigate(int)
beispielsweise durchspringen
findNavController().navigate(R.id.action_first_view_to_second_view)
findNavController().navigate(R.id.second_view)
Implementierung
Die grundlegende Zusammensetzung und das Funktionsprinzip der Navigation wurden im vorherigen Abschnitt vorgestellt. Anschließend werden wir das Thema eingeben, um die Navigation basierend auf der benutzerdefinierten Ansicht zu realisieren.
Folgendes muss erreicht werden:
- ViewNavigator
- Attribute für ViewNavigator
- ViewDestination
- NavigationHostView
- Diagrammdatei
ViewNavigator
Die Navigation bietet eine Möglichkeit zum Anpassen des Navigators: Verwenden Sie @Navigator.Name
Anmerkungen.
Wir definieren einen Namen für screen_view
den Navigator, in dem XML-Diagramm kann durch diesen Namen die entsprechende NavDestination definiert werden.
NavDestination und Navigator sind generisch eingeschränkt:Navigator<out NavDestination>
@Navigator.Name("screen_view")
class ViewNavigator(private val container: ViewGroup) : Navigator<ViewDestination>() {
override fun navigate(...) {
...}
override fun createDestination(): ViewDestination = ViewDestination(this)
override fun popBackStack(): Boolean {
...}
}
Attribute für ViewNavigator
Definieren Sie benutzerdefinierte Attribute, die in XML für Navigator verwendet layoutId
werden.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ViewNavigator">
<attr name="layoutId" format="reference" />
</declare-styleable>
</resources>
ViewDestination
@NavDestination.ClassType
Erlauben Sie uns, unsere eigenen zu definierenNavDestination
@NavDestination.ClassType(ViewGroup::class)
class ViewDestination(navigator: Navigator<out NavDestination>) : NavDestination(navigator) {
@LayoutRes var layoutId: Int = 0
override fun onInflate(context: Context, attrs: AttributeSet) {
super.onInflate(context, attrs)
context.resources.obtainAttributes(attrs, R.styleable.ViewNavigator).apply {
layoutId = getResourceId(R.styleable.ViewNavigator_layoutId, 0)
recycle()
}
}
}
In onInflate
, empfangen und analysieren , layoutId
den Wert des benutzerdefinierten Attributs
NavigationHostView
Definieren Sie die Implementierung von NavHost NavigationHostFrame
, die hauptsächlich zum Erstellen von Controllern verwendet wird, registrieren Sie den Navigatortyp und legen Sie Graph dafür fest
class NavigationHostFrame(...) : FrameLayout(...), NavHost {
private val navigationController = NavController(context)
init {
Navigation.setViewNavController(this, navigationController)
navigationController.navigatorProvider.addNavigator(ViewNavigator(this))
navigationController.setGraph(R.navigation.navigation)
}
override fun getNavController() = navigationController
}
Diagrammdatei
<screen_view/>
Definieren Sie in der Diagrammdatei NavDestination
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_navigation"
app:startDestination="@id/first_screen_view"
tools:ignore="UnusedNavigation">
<screen_view
android:id="@+id/first_screen_view"
app:layoutId="@layout/screen_view_first"
tools:layout="@layout/screen_view_first">
<action
android:id="@+id/action_first_screen_view_to_second_screen_view"
app:destination="@id/second_screen_view"
app:launchSingleTop="true"
app:popUpTo="@+id/first_screen_view"
app:popUpToInclusive="false" />
<action
android:id="@+id/action_first_screen_view_to_last_screen_view"
app:destination="@id/last_screen_view"
app:launchSingleTop="true"
app:popUpTo="@+id/first_screen_view"
app:popUpToInclusive="false" />
</screen_view>
<screen_view
android:id="@+id/second_screen_view"
app:layoutId="@layout/screen_view_second"
tools:layout="@layout/screen_view_second">
<action
android:id="@+id/action_second_screen_view_to_screen_view_third"
app:destination="@id/screen_view_third"
app:launchSingleTop="true"
app:popUpTo="@+id/main_navigation"
app:popUpToInclusive="true" />
</screen_view>
<screen_view
android:id="@+id/last_screen_view"
app:layoutId="@layout/screen_view_last"
tools:layout="@layout/screen_view_last" />
<screen_view
android:id="@+id/screen_view_third"
app:layoutId="@layout/screen_view_third"
tools:layout="@layout/screen_view_third" />
</navigation>
Öffnen Sie den Navigationseditor von Android Studio und sehen Sie das Ergebnis nach XML:
Setup in Aktivität
Verwenden Sie diese NavigationHostView schließlich als Container im Layout der Aktivität und ordnen Sie NavController im Code NavHost zu
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.my.sample.navigation.NavigationHostView
android:id="@+id/main_navigation_host"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navController = Navigation.findNavController(mainNavigationHost)
Navigation.setViewNavController(mainNavigationHost, navController)
}
Rufen Sie NavController in onBackPressed` auf, damit jedes NavDestination BackPress unterstützt
override fun onSupportNavigateUp(): Boolean = navController.navigateUp()
override fun onBackPressed() {
if (!navController.popBackStack()) {
super.onBackPressed()
}
}
Fazit
Die auf Fragment basierende Navigation bietet eine sofort einsatzbereite Implementierung und reserviert gleichzeitig eine erweiterbare Benutzeroberfläche durch Anmerkungen, die es Entwicklern erleichtert, die Implementierung anzupassen und sogar die vom Android Studio-Editor bereitgestellte Durchquerung zu genießen.
In den frühen Tagen der Instabilität von Fragment wurden viele Alternativen zu Fragment für die UI-Segmentierung angezeigt. Wenn diese Frameworks in Ihrem Projekt noch verwendet werden, können Sie die Navigation jetzt problemlos für sie anpassen ~