Navegación con Android [NavHostFragment]
En cuanto a la navegación de fragmentos en android existen diferentes maneras de hacerlo, sin embargo una reciente y aconsejada es el uso de NavHostFragment, en este post veremos algunos conceptos de navegación y como implementarlos con el NavHostFragment en un pequeño proyecto.
Referente a la navegación en android, existen 3 principios que podemos resumir:
1er Principio: Siempre hay un punto de inicio!
Cuando sea que iniciamos la app se mostrara una primera pantalla que será la que nosotros deseemos.
En nuestro caso nuestra aplicación contara con varios fragmentos siento el FragmentA nuestro punto inicio
Empezamos creando un proyecto en Android Studio con un blank activity, luego creamos nuestro primer fragmento
FragmentA
FragmentA.kt
package com.example.navhostfragmentdemo
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
class FragmentA : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_a, container, false)
}
}
Con su respectivo layout
fragment_a.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".FragmentA">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Fragment A"
android:textColor="@color/black"
android:textSize="48dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Go to fragment B" />
</LinearLayout>
De esta manera ya se tiene el FragmentA listo, faltando colocarlo en el layout del mainActivity de la siguiente forma
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<fragment
android:id="@+id/fragmentA"
android:name="com.example.navhostfragmentdemo.FragmentA"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Y el main activity lo dejamos tal y como fue creado
MainActivity.kt
package com.example.navhostfragmentdemo
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
Pueden hacer correr el proyecto y el resultado será el siguiente
Hasta ahora lo único que hicimos fue
- Crear proyecto
- Crear FragmentA
- Insertar el FragmentA en el layout del Main activity
2do Principio:Siempre puedes volver atrás (Backstack)
El backstack es una estructura de almacenamiento de tipo FILO(First In Last Out), que almacena nuestras vistas que tengamos en la app.
Sea nuestro punto de inicio el FragmentA, al ir hacia otra vista(FragmentB), esta nueva vista será puesta encima de nuestro punto inicial, y lo mismo cuando se vaya del FragmentB al FragmentC
Para la utilizar el navigation component debemos crear un resource de tipo Navigation que será el grafo contenedor de nuestros fragmentos
Nota: En caso de que al crear el resource no les salga el mensaje de error de las bibliotecas lo pueden agregar manualmente en el build.gradle de su proyecto
build.gradle
dependencies {
...
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
}
En el principio #1 insertamos el FragmentA en el activitymain.xml, si usamos el navigation architectural component, el NavHostFragment debe ser el contenedor de todos los fragmentos, por tanto debemos reemplazarlo
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity">
<fragment
android:id="@+id/myNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation" />
</LinearLayout>
Asignamos un nuevo nombre al fragmento "myNavHostFragment" y cambiamos el tipo a androidx.navigation.fragment.NavHostFragment
También aumentamos la línea
app:defaultNavHost="true"
que interceptara cuando sea que apretemos el back button de android.
La línea
app:navGraph="@navigation/navigation"
nos sirve para especificar que resource usara el NavHostFragment
Con los pasos anteriores realizados nos encontramos listos para manipular nuestro grafo con los fragments que deseemos
Haciendo click en el navigation.xml que creamos, nos aparecerá una pantalla vacío, y agregamos el punto inicial que sería nuestro FragmentA haciendo click en el botón de New destination
De igual manera que se creó que el "FragmentA" podemos crear un "FragmentB" de similar estructura y los vinculamos como se observa a continuación
Y al momento de crear sus vínculos, se crean acciones, las cuales las usaremos posteriormente
Están vinculados, pero no hay una acción que haga que de un fragment vaya a otro, por tanto , debemos implementar un clicklistener al botón del FragmentA
FragmentA
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_a, container, false)
val buttonA = view.findViewById<Button>(R.id.actionButton)
buttonA?.setOnClickListener{ view : View ->
Navigation.findNavController(view).navigate(R.id.action_fragmentA_to_fragmentB)
}
return view.rootView
}
Lo que hace la línea
Navigation.findNavController(view).navigate(R.id.action_fragmentA_to_fragmentB)
es encontrar el controlador de navegacion y navegar según la acción que se creó al momento de vincular el FragmentA con el FragmentB
Como resultado tenemos lo siguiente:
Podemos notar que no tenemos el botón para volver atrás dentro la app, pero si responde al back button del dispositivo.
Para arreglar este problema, cuando usemos NavHostFragment debemos hacer unos cambios en el activity contenedor de la navegación que en este caso es el MainActivity, los métodos onCreate y sobrescribir el método onSupportNavigateUp
MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = Navigation.findNavController(this, R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController)
}
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.myNavHostFragment).navigateUp()
|| super.onSupportNavigateUp()
}
En el onCreate se encuentra el controlador de navegación y lo incrustamos justo en el actionBar.
Luego el override al método onSupportNavigateUp devolverá la respuesta que nuestro componente de navegación retorne o el default en caso de no estar configurado
De tal manera ya estamos respetando el 2do principio de siempre poder volver atrás
3er principio: Up goes back (Mostly)
Este principio se basa en que existen dos maneras de volver atrás, una por el up button (flecha de la app que va atrás) y el back button(botón para volver del dispositivo).
El up button y el back button operan de la misma manera dentro la app, con la única excepción que cuando estemos en el punto de inicio, el FragmentA, el up button no tiene a donde mas volver atrás, así que simplemente desaparece o debería, pero el back button si funciona, saliendo de la aplicación.
Se agrego un FragmentC para terminar el experimento, funciona igual que el FragmentB, se muestra como funciona el up y el back button
Si desean mas información sobre esto pueden dirigirse a la documentacion:
- https://developer.android.com/guide/navigation/navigation-principles
- https://developer.android.com/guide/navigation/navigation-getting-started
El repositorio con todo lo avanzado
https://github.com/beldier/navHostFragmentIntro
Gracias por su atención, en otro post estaré explicando los safeargs, navegación condicional y otros truquitos.
Nos pasamos años sin vivir en absoluto, y de pronto toda nuestra vida se concentra en un solo instante (Wilde)
- Cristal
Comentarios: