La navegación es uno de los componentes importantes de Jetpack, que se utiliza para diseñar y organizar los saltos de página de la aplicación. Dado que la recomendación oficial es usar Framgent para llevar la implementación de la página, lo primero que me viene a la mente cuando se trata de Navegación es Fragmento. Pero, de hecho, Navigation también admite otros tipos de implementación de páginas, como la vista personalizada. Este artículo presentará el uso de la vista personalizada en la navegación.
Antes de la introducción formal, repasemos el uso básico de Navigation:
Estructura básica de navegación
El uso de la navegación involucra principalmente los siguientes objetos
- Graph
usa XML para diseñar la página de la aplicación (destino) y la ruta de salto entre cada página. Android Studio proporciona un editor para editar Graph - NavHost
NavHost es un contenedor que se utiliza para alojar todos los nodos del gráfico. La navegación proporciona una implementación predeterminada de NavHost para FragmentoNavHostFragment
. Se puede entender que todos los Fragmentos en el gráfico son ChildFragment. En el escenario de Vista personalizada presentado hoy, también existe la necesidad de implementar NavHost para la Vista personalizada. - Controlador
Cada NavHost tiene un controlador, que gestiona los saltos entre los nodos en el NavHost - Navigator
Controller implementa saltos específicos llamando a Navigator, y Navigator realiza implementaciones de saltos específicos
principio de funcionamiento
Cada página en Navegación es un Destino, que puede ser Fragmento, Actividad o Vista. Vincule el Destino de origen con el Destino de destino a través de Acción, y salte del Destino actual al Destino de destino a través de Acción.
Al igual que en la Actividad de lanzamiento, cuando se inicia la APLICACIÓN, es necesario definir un Destino de inicio como la visualización de la página de inicio.
Como se mencionó anteriormente, NavHost tiene implementaciones específicas para diferentes destinos, y NavController también tiene diferentes métodos de adquisición según el tipo de destino, pero todos son similares:
- Fragment.findNavController ()
- View.findNavController ()
- Activity.findNavController (viewId: Int)
Después de obtener el controlador, puede navigate(int)
saltar, por ejemplo
findNavController().navigate(R.id.action_first_view_to_second_view)
findNavController().navigate(R.id.second_view)
Implementación
La composición básica y el principio de funcionamiento de la navegación se introdujeron anteriormente, y luego ingresaremos al tema para realizar la navegación basada en la vista personalizada.
Es necesario lograr lo siguiente:
- ViewNavigator
- Atributos de ViewNavigator
- VerDestino
- NavigationHostView
- Archivo gráfico
ViewNavigator
Navigation proporciona una forma de personalizar Navigator: utilice @Navigator.Name
anotaciones.
Definimos un nombre para screen_view
el Navegador, en el xml Graph se puede definir con este nombre correspondiente NavDestination.
NavDestination y Navigator están restringidos por genéricos: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 {
...}
}
Atributos de ViewNavigator
Definir atributos personalizados usados en Xml para Navigator layoutId
,
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ViewNavigator">
<attr name="layoutId" format="reference" />
</declare-styleable>
</resources>
VerDestino
@NavDestination.ClassType
Permítanos definir el nuestroNavDestination
@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()
}
}
}
En onInflate
, recibe y analiza layoutId
el valor del atributo personalizado
NavigationHostView
Definir la implementación de NavHost NavigationHostFrame
, que se utiliza principalmente para crear un controlador, registrar el tipo de navegador y configurar Graph para él.
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
}
Archivo gráfico
En el archivo Graph, <screen_view/>
definiendo 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>
Abra el editor de navegación de Android Studio y vea el resultado después de Xml:
Configuración en actividad
Finalmente, use este NavigationHostView como un contenedor en el diseño de la Actividad y asocie NavController con NavHost en el código
<?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)
}
Llame a NavController en onBackPressed` para permitir que cada NavDestination admita BackPress
override fun onSupportNavigateUp(): Boolean = navController.navigateUp()
override fun onBackPressed() {
if (!navController.popBackStack()) {
super.onBackPressed()
}
}
Conclusión
La navegación basada en Fragment proporciona una implementación lista para usar, y al mismo tiempo reserva una interfaz extensible a través de anotaciones, lo que es conveniente para que los desarrolladores personalicen la implementación e incluso disfruten del recorrido que trae el editor de Android Studio.
En los primeros días de la inestabilidad de Fragment, aparecieron muchas alternativas a Fragment para la segmentación de la interfaz de usuario. Si estos marcos todavía se utilizan en su proyecto, ahora puede adaptar fácilmente Navigation para ellos ~