Ejemplo de implementación de un BottomNavigationView con el sistema de navegación del componente navigation del cojunto de componentes para Android (Jetpack) y que los fragmentos mostrados mantengan su estado
Partiendo de generar la plantilla con AndroidStudio seleccionar el BottomNavigation:
Dependencias
En el app.gradle añadir las siguientes dependencias
dependencies {
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
}
Layout principal
En el fragment nav_host_fragment se debe remover la asignación de la navegación, porque se implementa des de código
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="?attr/actionBarPopupTheme">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavView"
style="@style/Widget.MaterialComponents.BottomNavigationView.PrimarySurface"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Navegación
Los cambios es que antes de declarar con fragment se hace con keep_state_fragment
<?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/mobile_navigation"
app:startDestination="@+id/navigation_dashboard">
<keep_state_fragment
android:id="@+id/navigation_home"
android:name="app.webserveis.testbottomnavigation.ui.home.HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home" />
<keep_state_fragment
android:id="@+id/navigation_dashboard"
android:name="app.webserveis.testbottomnavigation.ui.dashboard.DashboardFragment"
android:label="@string/title_dashboard"
tools:layout="@layout/fragment_dashboard" />
<keep_state_fragment
android:id="@+id/navigation_notifications"
android:name="app.webserveis.testbottomnavigation.ui.notifications.NotificationsFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications" />
</navigation>
KeepStateNavigation
Método para mantener los estados de los fragmentos, comprueba en la pila del gestor si no existe el fragmento lo añade add si existe lo recupera quitando el actual con detach y attach con lo que se quiere mostrar
@Navigator.Name("keep_state_fragment") // `keep_state_fragment` is used in navigation xml
class KeepStateNavigator(
private val context: Context,
private val manager: FragmentManager,
private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {
override fun navigate(
destination: Destination,
args: Bundle?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): NavDestination? {
val tag = destination.id.toString()
val transaction : FragmentTransaction = manager.beginTransaction()
val currentFragment: Fragment? = manager.primaryNavigationFragment
var initialNavigate = false
if (currentFragment != null) {
transaction.detach(currentFragment)
} else {
initialNavigate = true
}
var fragment: Fragment? = manager.findFragmentByTag(tag)
if (fragment == null) {
fragment =
manager.fragmentFactory.instantiate(context.classLoader, destination.className)
transaction.add(containerId, fragment, tag)
} else {
transaction.attach(fragment)
}
transaction.apply {
setPrimaryNavigationFragment(fragment)
setReorderingAllowed(true)
}.commitNow()
return if (initialNavigate) {
destination
} else {
null
}
}
}
Implementación
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
supportActionBar?.title = getText(R.string.app_name)
setupNavigationKeepState()
}
fun setupNavigationKeepState() {
val navController = findNavController(R.id.nav_host_fragment)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)!!
val navigator = KeepStateNavigator(this, navHostFragment.childFragmentManager, R.id.nav_host_fragment)
navController.navigatorProvider += navigator
navController.setGraph(R.navigation.nav_graph)
bottomNavView.setupWithNavController(navController)
}
companion object {
val TAG: String = MainActivity::class.java.simpleName
}
}
Fragmentos
A cada fragmento que debemos asignar el titulo en onCreatedView
requireActivity().toolbar.title = "Home"
Enlaces
Solución modificada de Star-Zero Keep Navigation Kepp fragment Sample