2

I use Navigation component with a single activity. The idea to create custom toolbar for each Fragment. For example one toolbar has to be yellow, another purple with menu icons, another transparent. And also if it possible I would like to implement it with saving connection between navigation and toolbar by setupActionBarWithNavController(navController) in MainActivity. I tried to use this method (activity as? AppCompatActivity)?.setSupportActionBar(toolbar) in Fragment but it duplicated toolbar.

This is my theme

<style name="Theme.Movies" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>

This is toolbar in MainActivity layout

 <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/purple_700"
        android:elevation="4dp"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        app:layout_constraintTop_toTopOf="parent" />

This is MainActivity

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        setSupportActionBar(toolbar)

        val navHostFragment =
            supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment

        navController = navHostFragment.navController

        setupActionBarWithNavController(navController)
        bottom_nav.setupWithNavController(navController)

    }

    override fun onSupportNavigateUp(): Boolean {
        return navController.navigateUp() || super.onSupportNavigateUp()
    }
}
Zain
  • 37,492
  • 7
  • 60
  • 84
Artem
  • 93
  • 1
  • 6

1 Answers1

8

I tried to use this method (activity as? AppCompatActivity)?.setSupportActionBar(toolbar) in Fragment but it duplicated toolbar.

This is expected because the toolbar is hosted by the activity, and as Jetpack navigation architecture components uses a single-activity model, then all navGraph fragments hosted by this activity share this toolbar (because it's a part of the activity).

And therefore resetting the toolbar again will duplicate it.

The solution to this is to remove the toolbar from the activity, and use a unique toolbar within every fragment layout, this way when a fragment is transacted to another; the layout of the old fragment is replaced with the new layout of the new fragment, so the old toolbar is gone, and we've a new toolbar; and at this time you need to call setSupportActionBar(toolbar) for the new toolbar.

But notice that every time you call setSupportActionBar(toolbar) you have to recall setupActionBarWithNavController() because it needs to be attached to the new toolbar.

UPDATE

So, I have to call setSupportActionBar(toolbar) and setupActionBarWithNavController() in Fragment?

Actually you can call setSupportActionBar(toolbar) in the activity. But you can use requireActivity() in fragment to do that:

In order not to repeat things, you can have a function the activity to do that:

In activity:

fun setupActionBar(toolBar: Toolbar) {

    setSupportActionBar(toolbar)

    val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment

    navController = navHostFragment.navController

    setupActionBarWithNavController(navController)
    
}   

And in fragment:

val toolbar = findViewById<Toolbar>(R.id.foo)
(requireActivity() as MainActivity).setupActionBar(toolbar)
Zain
  • 37,492
  • 7
  • 60
  • 84
  • So, I have to call setSupportActionBar(toolbar) and setupActionBarWithNavController() in Fragment? – Artem Jun 27 '21 at 03:42
  • @Artem You can get the activity with `requireActivity()` in order to `setSupportActionBar`, please have a look at the UPDATE section in the answer – Zain Jun 27 '21 at 10:52
  • 1
    @Artem Hi, this works with bottomNavigationBar ? – misterios Nov 08 '21 at 14:26
  • @Zain What if I need the same thing with the navigation drawer/view? How can I set `mAppBarConfiguration` – MML Aug 23 '22 at 14:38
  • @MML Do you want different ToolBar for each fragment while using the navigation drawer? if yes that would have some challenges in the back stack of the fragments of the navigation drawer ..also the navigation drawer itself should be attached to a unique toolbar as it hosts the drawer toggle/burger button. – Zain Aug 23 '22 at 22:58
  • @MML pls have a look [here](https://stackoverflow.com/questions/68026718/navigation-component-how-to-show-a-normal-up-back-button-instead-of-hamburger-i/68038101#68038101), not sure if it would help in your case.. this is handling the toggle button of the drawer. – Zain Aug 23 '22 at 23:08
  • @zain yes I was need the custom toolbar for each fragment, I used your method like that `public void setupActionBar( Toolbar toolbar) { setSupportActionBar(toolbar); NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration); }` and I called it from each fragment `((MainActivity) requireActivity()).setupActionBar(binding.toolbar);` and it's working fine, if there's another way better than this please tell me – MML Aug 23 '22 at 23:19
  • 1
    @Zain no need to use `ActionBarDrawerToggle` in my case since the back stack is managed by this method on activity `@Override public boolean onSupportNavigateUp() { // NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_container); return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp(); }` – MML Aug 23 '22 at 23:22
  • 1
    @MML Unfortunately not, I didn't come across a better solution sorry. The problem that the ActionBar is a property of Activity, not a fragment; changing it must be through the activity; even without using the navigation components which just offers an automated management of the ActionBar. Hopefully that could be handled better in future APIs.  – Zai – Zain Aug 24 '22 at 03:00