104

How do I declare a menu inside of Android fragment? The method that I had used previously is now deprecated.

Originally:

 override fun onCreateView(...): View { setHasOptionsMenu(true) } 
 override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) this.menu = menu inflater.inflate(R.menu.menu, this.menu) } 
4
  • 9
    Did you read the release notes which linked to the code snippets? Commented Apr 18, 2022 at 23:01
  • 1
    Why exactly was it deprecated? I don't see anything special about the new code... Commented May 25, 2022 at 1:04
  • 3
    "The Fragment APIs for providing a menu to your activity’s ActionBar have been deprecated as they tightly couple your fragment to your activity and are not testable in isolation. ", from the release notes mentioned above. Commented Oct 12, 2022 at 20:32
  • @ianhanniballake I never read the release notes for updates I am forced to do. Google breaks something every time for no real reason anyway and if the application compiles nevertheless, the testing department will find it. The annual forced API updates are a huge mess. If I had known in 2015 that something like this was coming, I would probably have looked for a job without Android development... Commented Sep 27, 2024 at 6:36

10 Answers 10

148

From the Developer documentation, this can be achieved by the following:

/** * Using the addMenuProvider() API directly in your Activity **/ class ExampleActivity : ComponentActivity(R.layout.activity_example) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Add menu items without overriding methods in the Activity addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { // Add menu items here menuInflater.inflate(R.menu.example_menu, menu) } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { // Handle the menu selection return true } }) } } /** * Using the addMenuProvider() API in a Fragment **/ class ExampleFragment : Fragment(R.layout.fragment_example) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // The usage of an interface lets you inject your own implementation val menuHost: MenuHost = requireActivity() // Add menu items without using the Fragment Menu APIs // Note how we can tie the MenuProvider to the viewLifecycleOwner // and an optional Lifecycle.State (here, RESUMED) to indicate when // the menu should be visible menuHost.addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { // Add menu items here menuInflater.inflate(R.menu.example_menu, menu) } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { // Handle the menu selection return when (menuItem.itemId) { R.id.menu_clear -> { // clearCompletedTasks() true } R.id.menu_refresh -> { // loadTasks(true) true } else -> false } } }, viewLifecycleOwner, Lifecycle.State.RESUMED) } 

Fragments setHasOptionsMenu deprecated, use setHasOptionsMenu

Sign up to request clarification or add additional context in comments.

8 Comments

What about other functions, such as invalidation and getting a reference to the menu items? Those still exist, or also changed?
in fragment I would need to keep reference to the MenuProvider object so I can remove it by using removeMenuProvider(MenuProvider obj) cause of duplicated menus across multiple fragments ....you known how to do? Just tried it out but still have the issue
@riccardogabellone I got a similar issue, but I've noticed you can just pass the LifecycleOwner parameter to the addMenuProvider function, meaning in the fragment I use this: activity!!.addMenuProvider(menuProviderThatYouCreated, this.viewLifecycleOwner)
What does following mean?: // The usage of an interface lets you inject your own implementation
val menuHost: MenuHost = requireActivity() in fragment I see it's Required: MenuHost but Found: FragmentActivity
|
40

Expanding on what @joseph-wambura and @hammad-zafar-bawara said, you can also implement the interface in the fragment...

class MyFragment : Fragment(), MenuProvider { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { // Do stuff... val menuHost: MenuHost = requireActivity() menuHost.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED) } override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { menuInflater.inflate(R.menu.options, menu) // Do stuff... } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { // Do stuff... return false } } 

8 Comments

val menuHost: MenuHost = requireActivity() in fragment I see it's Required: MenuHost but Found: FragmentActivity How can use it?
Make sure that you are using import androidx.fragment.app.Fragment and not the depreciated import android.app.Fragment
I already use this
This is a forced ComponentActivity (and by extension its fragments) change. There are devs still using CompatActivity without the forced component framework and the guides do not explain how to accomplish this for that scenario. requireActivity() in that case throws a type mismatch.
The solution for this is cast it, like so val menuHost: MenuHost = requireActivity() as MenuHost
|
33

In Kotlin, declaration for Activity, Fragment and PreferenceFragmentCompat

Activity

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { menuInflater.inflate(R.menu.main_menu, menu) } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { // Handle the menu selection return true } }) } } 

Fragment

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // The usage of an interface lets you inject your own implementation val menuHost: MenuHost = requireActivity() menuHost.addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { // Add menu items here menuInflater.inflate(R.menu.main_menu, menu) } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { // Handle the menu selection return when (menuItem.itemId) { R.id.action_menu1 -> { // todo menu1 true } R.id.action_menu2 -> { // todo menu2 true } else -> false } } }, viewLifecycleOwner, Lifecycle.State.RESUMED) } 

PreferenceFragmentCompat

val menuHost: MenuHost = requireHost() as MenuHost //Same declaration with Fragment 

Use MenuProvider interface

class FirstFragment : Fragment(), MenuProvider { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val menuHost: MenuHost = requireActivity() menuHost.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED) } override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { // Add menu items here menuInflater.inflate(R.menu.second_menu, menu) } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { // Handle the menu selection return when (menuItem.itemId) { R.id.menu_clear -> { // Do stuff... true } R.id.menu_refresh -> { // Do stuff... true } else -> false } }, viewLifecycleOwner, Lifecycle.State.RESUMED) } 

5 Comments

Based from my tweet and personal blog notes, twitter.com/Codelaby/status/…
I tried but val menuHost: MenuHost = requireActivity() giving error: Type mismatch: inferred type is FragmentActivity but MenuHost was expected
After adding implementation 'androidx.activity:activity-compose:1.5.1' error disappeared thanx
Please can you tell me what are the purpose of these two`` viewLifecycleOwner, Lifecycle.State.RESUMED``
With this parameter, you can tell the menu at what point in the fragment's life cycle it should be visible (in this case, the resume state).
20

JAVA CODE

Option menu in Activity

addMenuProvider(new MenuProvider() { @Override public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { menuInflater.inflate(R.menu.bottom_nav_menu, menu); // Add menu options here } @Override public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { // Handle Menu Options Selection Here return false; } }); 

Option menu in Fragment

requireActivity().addMenuProvider(new MenuProvider() { @Override public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { menuInflater.inflate(R.menu.bottom_nav_menu, menu); // Add option Menu Here } @Override public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { return false; // Handle option Menu Here } }, viewLifecycleOwner, Lifecycle.State.RESUMED); 

6 Comments

in which method in fragment should use requireActivity().addMenuProvider?
I am using it in method "onCreateView".
I got Unresolved reference: addMenuProvider there's no method like this after requireActivity()
@Dr Mido try. MenuHost menuHost = requireActivity();. menuHost.addMenuProvider();
After adding implementation 'androidx.activity:activity-compose:1.5.1' error disappeared thanx
|
9

If you're using Jetpack NavigationUI then you need to setSupportActionBar(toolbar), otherwise the menu will not be present.

1 Comment

Correct. If using navigation graph then this is must. Otherwise it MenuProvider will not work.
6

this helps me on onCreateView Method:

requireActivity().addMenuProvider(new MenuProvider() { @Override public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { menuInflater.inflate(R.menu.bottom_nav_menu, menu); // Add option Menu Here } @Override public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { // Handle option Menu Here return false; } }, getViewLifecycleOwner, Lifecycle.State.RESUMED); 

1 Comment

What if the menuprovider is in the main activity and i only want to add some code when a menu item is clicked when the fragment is in view/focused? Do i also have to inflate in onCreateMenu?
5

I'm not sure why all answers recommend passing in Lifecycle.State.RESUME as the lifecycle state to the addMenuProvider call. This will make the menu disappear when the fragment is paused, which doesn't look great when the fragment is paused and still visible.

For example, showing a dialog as a result of tapping a menu item will make the menu disappear. It will re-appear when the dialog is dismissed.

For most cases, a better lifecycle state to pass in would be Lifecycle.State.CREATE, which will only remove the menu when the view is destroyed. This is also the default behaviour, so you can simply omit the lifecycle state.

Comments

3

For people who have flashbacks from Java, here is a bit better way using this extension function:

fun Fragment.addMenuProvider(@MenuRes menuRes: Int, callback: (id: Int) -> Boolean) { val menuProvider = object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { menuInflater.inflate(menuRes, menu) } override fun onMenuItemSelected(menuItem: MenuItem) = callback(menuItem.itemId) } (requireActivity() as MenuHost).addMenuProvider( menuProvider, viewLifecycleOwner, Lifecycle.State.RESUMED ) } 

and the usage:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) addMenuProvider(R.menu.feed_fragment) { when (it) { R.id.menu_write_post -> viewModel.goToPostCreation() R.id.menu_filter -> viewModel.goToFilter() else -> false } } } 

In case there is a need for feature flag, then you can repeat addMenuProvider call multiple times

1 Comment

this is actually really cool, because the callback is automatically removed when reaching onPause
2

in java for fragment i tried this . it works fine for me

 @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.layout_example, container, false); toolbar = (Toolbar)view.findViewById(R.id.toolbar_1); ((AppCompatActivity)getActivity()).setSupportActionBar(toolbar); MenuHost menuHost = requireActivity(); menuHost.addMenuProvider(new MenuProvider() { @Override public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { menuInflater.inflate(R.menu.menu_search,menu); } @Override public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { if (menuItem.getItemId() == R.id.search_friend){ Toast.makeText(getActivity(), "friends", Toast.LENGTH_SHORT).show(); return true; } else return false; } },getViewLifecycleOwner(), Lifecycle.State.RESUMED); return view; } 

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

Adding to the above answers, I prefer creating an extension function to reduce boilerplate code:

Extension function implementation:

fun Fragment.setupMenu(@MenuRes menuId: Int, onMenuSelected: (MenuItem) -> Boolean) = (requireActivity() as MenuHost).addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) = menuInflater.inflate(menuId, menu) override fun onMenuItemSelected(menuItem: MenuItem) = onMenuSelected(menuItem) }, viewLifecycleOwner, Lifecycle.State.RESUMED) 

This way, you will always insure you are adding the lifeCycleOwner and passing the correct lifecycle state which is Lifecycle.State.RESUMED

And you can simply use this inside fragment calling:

setupMenu(R.menu.menu_save) { menuItem -> when (menuItem.itemId) { R.id.save -> { viewModel.onSaveClicked() true } else -> false } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.