6

I am trying to set up a toolbar in a fragment.

Whilst the Google Developer docs have been updated to include Kotlin code (see this page):

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_my)
    // Note that the Toolbar defined in the layout has the id "my_toolbar"
    setSupportActionBar(findViewById(R.id.my_toolbar))

it relates to the setup of a toolbar in an activity as opposed to a fragment.

I found this SO post which suggests that you can't just call setSupportActionBar in a fragment. To quote:

Fragments don't have such method setSupportActionBar(). ActionBar is a property of Activity, so to set your toolbar as the actionBar, your activity should extend from ActionBarActivity and then you can call in your Fragment:

...

If you're using AppCompatActivity:

((AppCompatActivity)getActivity()).setSupportActionBar(mToolbar);

However the code given above is in java.

How do I call this in Kotlin?

Dennis 815
  • 139
  • 1
  • 2
  • 12
  • You shouldn't set the `toolbar` view in the Activity from its enclosed Fragment, wtf – EpicPandaForce Jun 25 '18 at 21:38
  • I don't understand what you mean "in the Activity from its enclosed Fragment"? I had my toolbar in the fragment layout because I thought that would then enable you to set up different menu items depending on the context (i.e. depending on the fragment that was onscreen). That's not necessary though based on Google's Navigation codelab. – Dennis 815 Jun 26 '18 at 15:22

2 Answers2

16

To access the ActionBar from a Fragment in Kotlin:

if(activity is AppCompatActivity){
        (activity as AppCompatActivity).setSupportActionBar(mToolbar)
    }

To set an ActionBar title from a Fragment you can do

(activity as AppCompatActivity).supportActionBar?.title = "Title"

or

(activity as AppCompatActivity).supportActionBar?.setTitle(R.string.my_title_string)
Bryan Dormaier
  • 800
  • 6
  • 10
  • Thanks! But this didn't seem to work when I put this code in the onCreateView function. – Dennis 815 Jun 26 '18 at 15:25
  • There is no guarantee that the activity is attached in onCreateView. You should wait until further in the lifecycle. Re-reading your question, I also wouldn't recommend putting your `Toolbar` in your `Fragment`. There are options to update the menu, etc from the `Fragment` that would give a likely better experience. – Bryan Dormaier Jun 26 '18 at 15:56
  • Yeah I have moved the `Toolbar` out of the fragment and I understand how to inflate different menu items for each fragment destination - it is so simple! However now my problem is figuring out how to change the title for each fragment. I think I need to call setTitle but can't work out how to do it in Kotlin or when (override fun super.onAttach?). Can you please help? – Dennis 815 Jun 27 '18 at 16:02
  • It should be like above, but `(activity as AppCompatActivity).supportActionBar?.title = "Title"` or `(activity as AppCompatActivity).supportActionBar?.setTitle(R.string.my_title_string)` – Bryan Dormaier Jun 27 '18 at 17:24
  • Brilliant - thank you so much!! I have put that in each fragment.kt file in onCreateOptionsMenu after inflating the menu items and it works fine. – Dennis 815 Jun 28 '18 at 12:33
2

There is an implementation in the Navigation codelab from Google that I think will do what I need: enable customisation of the title, menu items and hook into the up navigation for different fragment contexts. Specifically:

  1. The toolbar is included in the main layout xml file (navigation_activity.xml in that codelab) outside of the fragment:

navigation_activity.xml

<LinearLayout>
    <android.support.v7.widget.Toolbar/>
    <fragment/>
    <android.support.design.widget.BottomNavigationView/>
</LinearLayout>
  1. Then setup the toolbar in the main activity file as follows:

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private var drawerLayout: DrawerLayout? = null

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

        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        setSupportActionBar(toolbar)

        //...

        // Set up Action Bar
        val navController = host.navController
        setupActionBar(navController)

        //...

    }

    private fun setupActionBar(navController: NavController) {
        drawerLayout = findViewById(R.id.drawer_layout)

        NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        val retValue = super.onCreateOptionsMenu(menu)
        val navigationView = findViewById<NavigationView>(R.id.nav_view)
        // The NavigationView already has these same navigation items, so we only add
        // navigation items to the menu here if there isn't a NavigationView
        if (navigationView == null) {
            menuInflater.inflate(R.menu.menu_overflow, menu)
            return true
        }
        return retValue
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Have the NavHelper look for an action or destination matching the menu
        // item id and navigate there if found.
        // Otherwise, bubble up to the parent.
        return NavigationUI.onNavDestinationSelected(item,
                Navigation.findNavController(this, R.id.my_nav_host_fragment))
                || super.onOptionsItemSelected(item)
    }

    override fun onSupportNavigateUp(): Boolean {
        return NavigationUI.navigateUp(drawerLayout,
                Navigation.findNavController(this, R.id.my_nav_host_fragment))
    }
}
  1. Then in the fragment file you can inflate further menu items. In the codelab, main_menu.xml contains a shopping cart item which is added to the overflow setup in the main activity above.

MainFragment.kt

class MainFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                                  savedInstanceState: Bundle?): View? {
            setHasOptionsMenu(true)
            return inflater.inflate(R.layout.main_fragment, container, false)
    }

    override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
            inflater?.inflate(R.menu.main_menu, menu)
    }
}
Dennis 815
  • 139
  • 1
  • 2
  • 12