Disclaimer
Based of @Rajarshi original experiment, I made a working solution for this problem. I'm not sure is the most elegant, or if there are better ways. But after hours of research and investigation, this is the best solution I found.
Solution
Inflate the toolbars separately and store their references so they are not picked by the garbage collector.
Then load each on demand in your main AppBarLayout inside a custom OnDestinationChangedListener
defined for your navController
Example
Here's an example I've written in Kotlin.
On your activity.xml layout, define an AppBarLayout that is empty.
layout/activity.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
...
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay" />
...
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Define the toolbars that your app needs to have in separate layout files.
layout/toolbar_defaul.xml
<com.google.android.material.appbar.MaterialToolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/default_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="@menu/menu_default"
app:popupTheme="@style/AppTheme.PopupOverlay" />
layout/toolbar2.xml
<com.google.android.material.appbar.MaterialToolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar2"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="@menu/menu2"
app:popupTheme="@style/AppTheme.PopupOverlay" />
In your main (and only) activity, declare AppBar related components as class properties, so that they are not picked up by the garbage collector.
Activity.kt
class Activity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var appBarLayout: AppBarLayout
private lateinit var defaultToolbar: MaterialToolbar
private lateinit var toolbar2: MaterialToolbar
...
And finally, in the onCreate
method, define a OnDestinationChangedListener
for the navController. Use it to load on demand each toolbar.
Activity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ryvod)
// Set up AppBar
appBarLayout = findViewById(R.id.appbar)
appBarConfiguration = AppBarConfiguration(setOf(R.id.StartFragment))
defaultToolbar = layoutInflater.inflate(R.layout.toolbar_default, appBarLayout, false) as MaterialToolbar
toolbar2 = layoutInflater.inflate(R.layout.toolbar2, appBarLayout, false) as MaterialToolbar
val host =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
?: return
val navController = host.navController
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.locationPickerFragment -> {
appBarLayout.removeAllViews()
appBarLayout.addView(toolbar2)
setSupportActionBar(toolbar2)
}
else -> {
appBarLayout.removeAllViews()
appBarLayout.addView(defaultToolbar)
setSupportActionBar(defaultToolbar)
}
}
setupActionBarWithNavController(navController, appBarConfiguration)
}
}
That should do the trick