I am trying to add an item to the options menu from a group of fragments.
I have created a new MenuFragment
class and extended this for the fragments I wish to include the menu item in. Here is the code:
Java:
public class MenuFragment extends Fragment {
MenuItem fav;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
fav = menu.add("add");
fav.setIcon(R.drawable.btn_star_big_off);
}
}
Kotlin:
class MenuFragment : Fragment {
lateinit var fav: MenuItem
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
fav = menu.add("add");
fav.setIcon(R.drawable.btn_star_big_off);
}
}
For some reason the onCreateOptionsMenu
appears not to run.
Call the super method:
Java:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Add your menu entries here
super.onCreateOptionsMenu(menu, inflater);
}
Kotlin:
override fun void onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
// TODO Add your menu entries here
super.onCreateOptionsMenu(menu, inflater)
}
Put log statements in the code to see if the method is not being called or if the menu is not being amended by your code.
Also ensure you are calling setHasOptionsMenu(boolean)
in onCreate(Bundle)
to notify the fragment that it should participate in options menu handling.
I had the same problem, but I think it's better to summarize and introduce the last step to get it working:
Add setHasOptionsMenu(true) method in your Fragment's onCreate(Bundle savedInstanceState) method. Override onCreateOptionsMenu(Menu menu, MenuInflater inflater) (if you want to do something different in your Fragment's menu) and onOptionsItemSelected(MenuItem item) methods in your Fragment. Inside your onOptionsItemSelected(MenuItem item) Activity's method, make sure you return false when the menu item action would be implemented in onOptionsItemSelected(MenuItem item) Fragment's method.
An example:
Activity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.activity_menu_item:
// Do Activity menu item stuff here
return true;
case R.id.fragment_menu_item:
// Not implemented here
return false;
default:
break;
}
return false;
}
Fragment
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
....
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Do something that differs the Activity's menu here
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.activity_menu_item:
// Not implemented here
return false;
case R.id.fragment_menu_item:
// Do Fragment menu item stuff here
return true;
default:
break;
}
return false;
}
setHasOptionsMenu(true);
Thanks
If you find the onCreateOptionsMenu(Menu menu, MenuInflater inflater)
method is not being invoked, make sure you call the following from the Fragment's onCreate(Bundle savedInstanceState)
method:
setHasOptionsMenu(true)
Toolbar
and had to call setHasOptionsMenu(true)
in onCreateView()
instead onCreate()
to get it done.
If you need a menu
to refresh a webview
inside a specific Fragment
, you can use:
Fragment:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Add your menu entries here
inflater.inflate(R.menu.menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.exit:
System.exit(1);
break;
case R.id.refresh:
webView.reload();
break;
}
return true;
}
menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/exit" android:title="Exit" android:icon="@drawable/ic_action_cancel" />
<item android:id="@+id/refresh" android:title="Refresh" android:icon="@drawable/ic_action_refresh" />
</menu>
TL;DR
Use the android.support.v7.widget.Toolbar
and just do:
toolbar.inflateMenu(R.menu.my_menu)
toolbar.setOnMenuItemClickListener {
onOptionsItemSelected(it)
}
Standalone Toolbar
Most of the suggested solutions like setHasOptionsMenu(true)
are only working when the parent Activity has the Toolbar in its layout and declares it via setSupportActionBar()
. Then the Fragments can participate in the menu population of this exact ActionBar:
Fragment.onCreateOptionsMenu(): Initialize the contents of the Fragment host's standard options menu.
If you want a standalone toolbar and menu for one specific Fragment you can to do the following:
menu_custom_fragment.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_save"
android:title="SAVE" />
</menu>
custom_fragment.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
...
CustomFragment.kt
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(layout.custom_fragment, container, false)
val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
toolbar.inflateMenu(R.menu.menu_custom_fragment)
toolbar.setOnMenuItemClickListener {
onOptionsItemSelected(it)
}
return view
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_save -> {
// TODO: User clicked the save button
true
}
else -> super.onOptionsItemSelected(item)
}
}
Yes, it's that easy. You don't even need to override onCreate()
or onCreateOptionsMenu()
.
PS: This is only working with android.support.v4.app.Fragment
and android.support.v7.widget.Toolbar
(also be sure to use AppCompatActivity
and an AppCompat
theme in your styles.xml
).
onPrepareOptionsMenu
in this case?
In the menu.xml
you should add all the menu items. Then you can hide items that you don't want to see in the initial loading.
menu.xml
<item
android:id="@+id/action_newItem"
android:icon="@drawable/action_newItem"
android:showAsAction="never"
android:visible="false"
android:title="@string/action_newItem"/>
Add setHasOptionsMenu(true)
in the onCreate() method to invoke the menu items in your Fragment class.
FragmentClass.java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
You don't need to override onCreateOptionsMenu
in your Fragment class again. Menu items can be changed (Add/remove) by overriding onPrepareOptionsMenu
method available in Fragment.
@Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.action_newItem).setVisible(true);
super.onPrepareOptionsMenu(menu);
}
In my case, here is the steps.
Step-1
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Here notify the fragment that it should participate in options menu handling.
setHasOptionsMenu(true);
}
Step-2
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// First clear current all the menu items
menu.clear();
// Add the new menu items
inflater.inflate(R.menu.post_stuff, menu);
super.onCreateOptionsMenu(menu, inflater);
}
Step-3
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.post_stuff:
Log.d(TAG, "Will post the photo to server");
return true;
case R.id.cancel_post:
Log.d(TAG, "Will cancel post the photo");
return true;
default:
break;
}
return super.onOptionsItemSelected(item);
}
menu.clear()
cleared previous menu. Worked for me :)
You need to use menu.clear() before inflating menus.
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
and
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
If you want to add your menu custom
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_custom, menu);
}
I've had the same problem, my fragments were pages of a ViewPager. The reason it was happening is that I was using child fragment manager rather than the activity support fragment manager when instantiating FragmentPagerAdapter.
Menu file:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/play"
android:titleCondensed="Speak"
android:showAsAction="always"
android:title="Speak"
android:icon="@drawable/ic_play">
</item>
<item
android:id="@+id/pause"
android:titleCondensed="Stop"
android:title="Stop"
android:showAsAction="always"
android:icon="@drawable/ic_pause">
</item>
</menu>
Activity code:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.speak_menu_history, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.play:
Toast.makeText(getApplicationContext(), "speaking....", Toast.LENGTH_LONG).show();
return false;
case R.id.pause:
Toast.makeText(getApplicationContext(), "stopping....", Toast.LENGTH_LONG).show();
return false;
default:
break;
}
return false;
}
Fragment code:
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.play:
text = page.getText().toString();
speakOut(text);
// Do Activity menu item stuff here
return true;
case R.id.pause:
speakOf();
// Not implemented here
return true;
default:
break;
}
return false;
}
Your code is fine. Only the super was missing in the method :
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO add your menu :
inflater.inflate(R.menu.my_menu, menu);
//TODO call super
super.onCreateOptionsMenu(menu, inflater);
}
I was getting crazy because none of the answers here worked for me.
To show the menu I had to call: setSupportActionBar(toolbar)
Done!
Note: if your toolbar
view isn't in the same activity layout you can't use the call above directly from your activity class, in this case you'll need to get that activity from your fragment class and then call the setSupportActionBar(toolbar)
. Remembering: your activity class should extends the AppCompatActivity.
Hope that this answer help you.
Set setHasMenuOptions(true)
works if application has a theme with Actionbar
such as Theme.MaterialComponents.DayNight.DarkActionBar
or Activity has it's own Toolbar, otherwise onCreateOptionsMenu
in fragment does not get called.
If you want to use standalone Toolbar you either need to get activity and set your Toolbar as support action bar with
(requireActivity() as? MainActivity)?.setSupportActionBar(toolbar)
which lets your fragment onCreateOptionsMenu to be called.
Other alternative is, you can inflate your Toolbar's own menu with toolbar.inflateMenu(R.menu.YOUR_MENU)
and item listener with
toolbar.setOnMenuItemClickListener {
// do something
true
}
Setting the options menu after creating the fragment view worked well for me.
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
}
My problem was slightly different. I did everything right. But I was inheriting the wrong class for the activity hosting the fragment.
So to be clear, if you are overriding onCreateOptionsMenu(Menu menu, MenuInflater inflater)
in the fragment, make sure your activity class which hosts this fragment inherits android.support.v7.app.ActionBarActivity
(in case you would want to support below API level 11).
I was inheriting the android.support.v4.app.FragmentActivity
to support API level below 11.
One thing I would add to this and the reason it was not working for me.
It is similar to Napster's answer.
Make sure your fragment's hosting activity extends AppCompatActivity, not FragmentActivity! public class MainActivity extends AppCompatActivity { } From the Google Reference Documentation for FragmentActivity: Note: If you want to implement an activity that includes an action bar, you should instead use the ActionBarActivity class, which is a subclass of this one, so allows you to use Fragment APIs on API level 7 and higher. To update Napster's answer -- ActionBarActivity now being deprecated, use AppCompatActivity instead. When using AppCompatActivity, also make sure you set "the activity theme toTheme.AppCompat or a similar theme" (Google Doc).
Note: android.support.v7.app.AppCompatActivity
is a subclass of the android.support.v4.app.FragmentActivity
class (see AppCompatActivity ref doc).
In your menu folder make a .menu xml file and add this xml
<item
android:id="@+id/action_search"
android:icon="@android:drawable/ic_menu_search"
android:title="@string/action_search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always|collapseActionView" />
In your fragment class overide this method and
implement SearchView.OnQueryTextListener in your fragment class
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setHasOptionsMenu(true);
}
Now just setup your menu xml file in fragment class
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_main, menu);
final MenuItem item = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView)
MenuItemCompat.getActionView(item);
MenuItemCompat.setOnActionExpandListener(item,
new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// Do something when collapsed
return true; // Return true to collapse action view
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
// Do something when expanded
return true; // Return true to expand action view
}
});
}
If all of the above does not work, you need to debug and make sure function onCreateOptionsMenu has been called (by placing debug or write log...)
If it's not run, maybe your Android theme is not supporting the action bar. Open AndroidManifest.xml and set the value for android:theme
with theme support action bar:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat">
on your onCreate method add setHasOptionMenu()
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
Then override your onCreateOptionsMenu
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add("Menu item")
.setIcon(android.R.drawable.ic_delete)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
This is what I did to hide and unhide ALL the menu options when the fragment is loaded and destroyed, respectively. It takes the risk out of pointing to null
for R.id.your_menu_item
and allowed me to reuse the fragment elsewhere.
lateinit var optionsMenu: Menu
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menu.iterator().forEach {
it.isVisible = false
}
optionsMenu = menu
super.onCreateOptionsMenu(menu, inflater)
}
override fun onDestroyView() {
optionsMenu.iterator().forEach {
it.isVisible = true
}
super.onDestroyView()
}
In case none of the options above work for you, try this in your fragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
....
toolBar = rootView.findViewById(R.id.import_contacts_toolbar)
toolBar?.title = "Your title"
toolBar?.subtitle = "yor subtitile"
contactsActivity().setSupportActionBar(toolBar)
toolBar?.inflateMenu(R.menu.import_contacts_menu)
...
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.1 -> {
return true
}
R.id.2 -> {
return true
}
}
return false
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menu.clear()
inflater.inflate(R.menu.import_contacts_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
val search = menu.findItem(R.id.action_search)
val searchView = search.actionView as SearchView
searchView.requestFocus()
val txtSearch = searchView.findViewById<View>(androidx.appcompat.R.id.search_src_text) as EditText
txtSearch.hint = "Search..."
txtSearch.setHintTextColor(Color.WHITE);
txtSearch.setTextColor(Color.WHITE)
try {
val f: Field = TextView::class.java.getDeclaredField("mCursorDrawableRes")
f.setAccessible(true)
f.set(txtSearch, R.drawable.search_edit_text_cursor)
} catch (ignored: Exception) {
Log.d(TAG, "failed to expose cursor drawable $ignored")
}
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
return true
}
})
searchView.setOnCloseListener {
}
}
In my case I had a search menu item which was set to always visible. This is the xml for it:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
app:showAsAction="always"
app:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="Search"/>
<item android:id="@+id/1"
android:title="1">
</item>
<item android:id="@+id/2"
android:title="2">
</item>
</menu>
There is a new way of doing this since androidx.activity:activity:1.4.0
You are supposed to use the MenuProvider API.
It is used as follows:
Instead of calling super.setHasOptionMenu
and implementing onCreateOptionsMenu
you are supposed to call addMenuProvider
in onViewCreated
.
An Example:
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 true
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
source: Activity Changelog
Success story sharing