La navigation est l'un des composants importants de Jetpack, utilisé pour concevoir et organiser les sauts de page d'application. Étant donné que la recommandation officielle est d'utiliser Framgent pour réaliser l'implémentation de la page, la première chose qui vient à l'esprit en matière de navigation est Fragment. Mais en fait, Navigation prend également en charge d'autres types d'implémentations de page, telles que les vues personnalisées. Cet article présentera l'utilisation de la vue personnalisée dans la navigation.
Avant l'introduction officielle, passons en revue l'utilisation de base de la navigation:
Structure de base de la navigation
L'utilisation de la navigation implique principalement les objets suivants
- Graph
utilise XML pour concevoir la page APP (destination) et le chemin de saut entre chaque page. Android Studio fournit un éditeur pour modifier le graphique - NavHost
NavHost est un conteneur utilisé pour héberger tous les nœuds du graphique. La navigation fournit une implémentation par défaut de NavHost pour FragmentNavHostFragment
. On comprend que tous les fragments du graphe sont ChildFragment. Dans le scénario de vue personnalisé présenté aujourd'hui, il est également nécessaire de mettre en œuvre NavHost pour une vue personnalisée. - Contrôleur
Chaque NavHost a un contrôleur, qui gère le saut entre les nœuds dans le NavHost - Navigator
Controller implémente des sauts spécifiques en appelant Navigator, et Navigator entreprend des implémentations de saut spécifiques
principe de fonctionnement
Chaque page de Navigation est une destination, qui peut être Fragment, Activité ou Vue. Liez la destination source à la destination de destination via l'action, et passez de la destination actuelle à la destination de destination via l'action.
Semblable à l'activité de lancement, lorsque l'application démarre, une destination de départ doit être définie comme affichage de la page d'accueil.
Comme mentionné précédemment, NavHost a des implémentations spécifiques pour différentes destinations. NavController propose également différentes méthodes d'acquisition en fonction du type de destination, mais elles sont toutes similaires:
- Fragment.findNavController()
- View.findNavController ()
- Activity.findNavController (viewId: Int)
Après avoir obtenu le contrôleur, vous pouvez navigate(int)
passer, par exemple
findNavController().navigate(R.id.action_first_view_to_second_view)
findNavController().navigate(R.id.second_view)
la mise en oeuvre
La composition de base et le principe de fonctionnement de la navigation ont été présentés ci-dessus, puis nous entrerons dans le sujet pour réaliser la navigation basée sur une vue personnalisée.
Les éléments suivants doivent être atteints:
- AfficherNavigator
- Attributs pour ViewNavigator
- AfficherDestination
- NavigationHostView
- Fichier graphique
AfficherNavigator
La navigation offre un moyen de personnaliser Navigator: utilisez des @Navigator.Name
annotations.
Nous définissons un nom pour screen_view
le navigateur, dans le graphique xml peut être défini par ce nom correspondant NavDestination.
NavDestination et Navigator sont limités par des génériques: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 {
...}
}
Attributs pour ViewNavigator
Définir les attributs personnalisés utilisés dans Xml pour Navigator layoutId
,
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ViewNavigator">
<attr name="layoutId" format="reference" />
</declare-styleable>
</resources>
AfficherDestination
@NavDestination.ClassType
Permettez-nous de définir le nôtreNavDestination
@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()
}
}
}
Dans onInflate
, recevez et analysez layoutId
la valeur de l' attribut personnalisé
NavigationHostView
Définir l'implémentation de NavHost NavigationHostFrame
, qui est principalement utilisé pour créer un contrôleur, enregistrer le type de navigateur et définir un graphique pour celui-ci
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
}
Fichier graphique
Dans le fichier Graph, en <screen_view/>
définissant 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>
Ouvrez l'éditeur de navigation d'Android Studio et voyez le résultat après Xml:
Configuration en activité
Enfin, utilisez ce NavigationHostView comme conteneur dans la disposition de l'activité et associez NavController à NavHost dans le code
<?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)
}
Appelez NavController dans onBackPressed` pour permettre à chaque NavDestination de prendre en charge BackPress
override fun onSupportNavigateUp(): Boolean = navController.navigateUp()
override fun onBackPressed() {
if (!navController.popBackStack()) {
super.onBackPressed()
}
}
Conclusion
La navigation basée sur Fragment fournit une implémentation prête à l'emploi et réserve en même temps une interface extensible via des annotations, ce qui est pratique pour les développeurs pour personnaliser l'implémentation et même profiter de la traversée apportée par l'éditeur Android Studio.
Au début de l'instabilité de Fragment, de nombreuses alternatives à Fragment sont apparues pour la segmentation de l'interface utilisateur. Si ces frameworks sont toujours utilisés dans votre projet, vous pouvez désormais adapter la navigation pour eux avec plaisir ~