Agung
Agung

Reputation: 13843

how to setup different toolbar using navigation controller component?

I am actually not sure, how the correct way or the best practice to set different toolbar using navigation controller component

in my app. I want to set 2 different toolbars.

  1. green toolbar
  2. red toolbar

two toolbars with different colour. this is just to simplify the case, actually I have multiple toolbars

I am using navigation controller component. and currently on my Main Activity as the host, I set the green toolbar in my main activity using this code

        setSupportActionBar(green_toolbar)
        supportActionBar?.setDisplayShowTitleEnabled(false)

        // set up top hierarchy destination
        val appBarConfiguration = AppBarConfiguration(setOf(
            R.id.destination_home,
            R.id.destination_search,
            R.id.destination_user_control,
            R.id.destination_create_event)
        )

        green_toolbar.setupWithNavController(navController,appBarConfiguration)

so what is the best way to set different toolbar using navigation controller component ?

do I have to make those 2 different toolbars in my main activity? or do I have to set fragmentY destination(that has red toolbar) as another activity not as the fragment ?

or.... I don't know....please help :)

Upvotes: 24

Views: 21970

Answers (5)

virtual-adam
virtual-adam

Reputation: 11

Managed to get the outcome I wanted using parts of the answers above.

I left the nav drawer code in the main activity and the toolbar code in the main xml, then modified the setUpToolbar() function as follows...

private fun setUpToolbar() {
    val mainActivity = mActivity as MainActivity
    val toolbar = requireView().findViewById<Toolbar>(R.id.settings_toolbar)    
    val navController = NavHostFragment.findNavController(this)
    val appBarConfiguration = mainActivity.appBarConfiguration

    mainActivity.setSupportActionBar(toolbar)  
    NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)
}

Upvotes: 0

Agung
Agung

Reputation: 13843

use the code below if your app using Navigation Drawer, if you are using Bottom Navigation View, then use this answer

first, you have to remove the toolbar from MainActivity, and you have to set the toolbar in each your fragment xml. if you set toolbar in each fragment, it also will make it possible to implement Collapsing Toolbar. from the documentation in here, it is said

If, however, your top app bar changes substantially across destinations, then consider removing the top app bar from your activity and defining it in each destination fragment, instead.

remove the toolbar from your MainActivity. set your MainActivity xml like this

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start"
    tools:context=".MainActivity">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">


        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navGraph="@navigation/main_graph" />


    </androidx.constraintlayout.widget.ConstraintLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/nav_drawer_header"
        app:menu="@menu/nav_drawer_menu" />

</androidx.drawerlayout.widget.DrawerLayout>

for example, my app will be like this

enter image description here

it has 3 top level destinations: message, chat and share

then set your MainActivity like this

class MainActivity : AppCompatActivity() {

    lateinit var navController : NavController
    lateinit var appBarConfiguration: AppBarConfiguration


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

        appBarConfiguration = AppBarConfiguration(setOf(
            R.id.destination_share,
            R.id.destination_message, // set all your top level destinations in here
            R.id.destination_chat), // don't forget the parentheses
            drawer_layout // include your drawer_layout
        )


        navController = Navigation.findNavController(this,R.id.nav_host_fragment)

    }


    override fun onSupportNavigateUp(): Boolean {
        return NavigationUI.navigateUp(navController,appBarConfiguration)
    }

    
}

in EACH top level fragment, set it using the code below

class ChatFragment : Fragment() { // for Message and Share Fragment, will be the same

    lateinit var mActivity : FragmentActivity

    override fun onAttach(context: Context) {
        super.onAttach(context)

        activity?.let { mActivity = it }

    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_chat, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        setUpToolbar()
        
    }

    private fun setUpToolbar() {

        val mainActivity = mActivity as MainActivity

        mainActivity.setSupportActionBar(toolbar)
        val navController = NavHostFragment.findNavController(this)
        val appBarConfiguration = mainActivity.appBarConfiguration
        NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)

    }


}

and in the child fragment, i.e if you want to show back button instead of hamburger button in your fragment, then use the same code as above, except the setToolbar method will be different, like this

private fun setUpToolbar() {

        val mainActivity = mActivity as MainActivity
        val navigationView: NavigationView = mActivity.findViewById(R.id.navigation_view)

        mainActivity.setSupportActionBar(toolbar)
        val navController = NavHostFragment.findNavController(this)
        NavigationUI.setupActionBarWithNavController(mainActivity,navController)
        NavigationUI.setupWithNavController(navigationView,navController)

    }

and if your toolbar has menu, then add this code in setToolbar method:

setHasOptionsMenu(true)

toolbar.setNavigationOnClickListener { view ->
    view.findNavController().navigateUp()
}

to use AppBarConfiguration class , in gradle app you need to use navigation-ui-ktx artifact and you need to add compile options and kotlin options like this

android {
   

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }

}


dependencies {

    def nav_version = "2.3.0-alpha04"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

don't forget to add noActionBar in your res value style xml

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">


        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>


    </style>

Upvotes: 9

Agung
Agung

Reputation: 13843

The answer below is for an app that use Bottom Navigation View, if you are using Navigation Drawer then use this anwer

so according the documentation from here , I need to set the toolbar in each fragment.

If, however, your top app bar changes substantially across destinations, then consider removing the top app bar from your activity and defining it in each destination fragment, instead.

so we will add the toolbar in each fragment instead of set it in MainActivity. if you set toolbar in each fragment, it also will make it possible to implement Collapsing Toolbar.

say for example in your bottom navigation menu you have home, profile and search fragments as top level fragment (root) like this

enter image description here

so in EACH top level fragment set the toolbar using this code in onViewCreated of your fragment.

val toolbar = view.findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbarInYourFragment)
val appBarConfiguration = AppBarConfiguration(setOf(
    R.id.destination_home,
    R.id.destination_profile  // set all your top level destinations in here
    R.id.destination_search)
)

val navHostFragment = NavHostFragment.findNavController(this);
NavigationUI.setupWithNavController(toolbar, navHostFragment,appBarConfiguration)

you have to pass appBarConfiguration to remove back button in your toolbar. so you have to pass appBarConfiguration in each top level fragment (home,search,profile), not only in your home.

and for the rest of the fragments you don't need to pass appBarConfiguration, so for the rest of your fragments just pass this code in onViewCreated .

val toolbar = view.findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbarInYourFragment)
val navHostFragment = NavHostFragment.findNavController(this);
NavigationUI.setupWithNavController(toolbar, navHostFragment)

and if your toolbar has menu, then add this code:

setHasOptionsMenu(true)

(activity as AppCompatActivity).setSupportActionBar(toolbar)

toolbar.setNavigationOnClickListener { view ->
    view.findNavController().navigateUp()
}

to use AppBarConfiguration class , in gradle app you need to use navigation-ui-ktx artifact and you need to add compile options and kotlin options like this

android {
   

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }

}


dependencies {

    def nav_version = "2.3.0-alpha04"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

don't forget to add noActionBar in your res value style xml

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">


        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>


    </style>


  

Upvotes: 35

Horun Djavariev
Horun Djavariev

Reputation: 43

You must set separate toolbar on each fragments, but it a little bit confusing. I used library for solving this problem.

Use the navigation component in the same way. For every fragments do this.

Create toolbar in layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize" />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <Button
            android:id="@+id/btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="20dp"
            android:background="?colorPrimary"
            android:text="Next fragment"
            android:textColor="?android:textColorSecondary" />

    </FrameLayout>

</LinearLayout>

And use 'setupToolbar' method in fragment

class FirstFragment : Fragment(R.layout.fragment_first) {

    ...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setupToolbar(toolbar)
    }  

    ...

}

and don't forget to implement lib on your dependencies, but be careful this lib isn't on release version, and may be changed.

repositories {
    ...
    maven { url 'https://jitpack.io' }
}

dependencies {
    implementation 'com.github.AlexanderGuru:MultipleToolbars:0.0.1-SNAPSHOT'
}

details: https://github.com/AlexanderGuru/MultipleToolbars

Upvotes: 0

Pavel Berdnikov
Pavel Berdnikov

Reputation: 303

It whould be more convenient to leave MainActivity layout with just navigation fragment and define needed toolbars in fragments layouts. Then in each fragment onCreateView:

(activity as AppCompatActivity).setSupportActionBar(toolbar)

toolbar.setNavigationOnClickListener { view ->
    view.findNavController().navigateUp()
}

For better example see how this is done in Android-Sunflower app by google: https://github.com/googlesamples/android-sunflower

Upvotes: 1

Related Questions