Update 2023-06-10:
Below is no longer working since androidx.navigation:navigation-fragment-ktx:2.6.0
Original post
I tried STAR_ZERO's solution https://github.com/STAR-ZERO/navigation-keep-fragment-sample for several hours and found it not working in my app first of all.
Finally succeeded to achieve what I wanted: My main Fragment
in main Activity
should not be re-created each time when navigating away and then back using NavigationBarView
or BottomNavigationView
.
Limitations:
- Works only exactly with the nav_graph's startDestination (here:
app:startDestination="@id/nav_home"
), Note: All other fragments require the <keep_state_fragment ...>
too!
- Using setOnItemSelectedListener (NavigationBarView.OnItemSelectedListener listener) seems to conflict with the intention to not re-create a fragment
Using these versions:
dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:2.5.3"
implementation "androidx.navigation:navigation-ui-ktx:2.5.3"
}
layout/activity_main.xml
...
<!-- Do NOT add app:navGraph="@navigation/nav_graph" -->
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
...
navigation/nav_graph.xml
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/nav_home">
<!-- ALL fragments must be keep_state_fragment,
otherwise nav_home will not behave as keep_state_fragment -->
<keep_state_fragment
android:id="@+id/nav_home"
... />
<keep_state_fragment
android:id="@+id/nav_other"
... />
KeepStateNavigator.kt (yes ... nearly empty class)
@Navigator.Name("keep_state_fragment") // 'keep_state_fragment' is used in navigation/nav_graph.xml
class KeepStateNavigator(
private val context: Context,
private val manager: FragmentManager, // MUST pass childFragmentManager.
private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {
/* NOTE: override fun navigate(...) is never called, so not needed */
}
MainActivity.java
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
Fragment navHostFragment = getSupportFragmentManager()
.findFragmentById(R.id.nav_host_fragment);
FragmentManager childFragmentManager = navHostFragment.getChildFragmentManager();
// Add our KeepStateNavigator to NavController's NavigatorProviders
navController.getNavigatorProvider().addNavigator(
new KeepStateNavigator(this, childFragmentManager,
R.id.nav_host_fragment));
// must be here, not in layout/activity_main.xml,
// because we create KeepStateNavigator after NavigationBarView was inflated
navController.setGraph(R.navigation.nav_graph);
// Do NOT set a NavigationBarView.OnItemSelectedListener
// Seems to conflict with the intention to not re-create Fragment
// DO NOT: *NavigationBarView*.setOnItemSelectedListener(...);
// Done.
NavigationUI.setupWithNavController(navBarView, navController);
}