Error calling setSupportActionBar in a Fragment in Kotlin
Asked Answered
S

2

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?

Sermonize answered 24/6, 2018 at 8:27 Comment(2)
You shouldn't set the toolbar view in the Activity from its enclosed Fragment, wtfErasmo
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.Sermonize
C
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)
Chenille answered 25/6, 2018 at 21:35 Comment(5)
Thanks! But this didn't seem to work when I put this code in the onCreateView function.Sermonize
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.Chenille
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?Sermonize
It should be like above, but (activity as AppCompatActivity).supportActionBar?.title = "Title" or (activity as AppCompatActivity).supportActionBar?.setTitle(R.string.my_title_string)Chenille
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.Sermonize
S
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)
    }
}
Sermonize answered 26/6, 2018 at 15:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.