I'm a Java programmer with experience. I started working on a small Android project and i got soooo frustrated by how much Android has complicated programming just to be more flexible.
Exactly, the problem i have is as follows: i have a menu item that i need to use in several menus inside mu app. Is there a way to create a menu item object (possible associate some function to it) and just pass it around so any activity can add it to it's own contex menu?
I have tried this:
Created a "res.menu.mymenu.xml" file that contains a menu item with the same id as menu item from another "res.menu.mymenu2.xml" file. Then i have tried this:
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(getMenuToInflate(), menu);
menu.add(0, R.id.mnhidefrom, 0, ((MenuItem)findViewById(R.id.mnhidefrom)).getTitle());
HideMenuAction.prepareAndGetHideMeMenuItem(menu, menuInfo); //creating an intent for "hide" menu item that will hold some data needed when user clicks the menu item.
}
But no luck... I got "null pointer exception" on "menu.add" line...
Is there a normal, object oriented way, to create a menu item that will know what it should do regardless of anything outside (like,say, menu it belongs to) and just pass it around like any other object and add it to any menu i like (just like i can do with Swing menu and JMenuItems)?
Ahmad has half the story. This will give you a base of menu options that are always available. The way to add menu options specific to each extending activity is to write a Fragment for it. The fragment will included in the activity layout, but you can make it take up no room or use it for padding. When a Fragment implements onCreateOptionsMenu, the choices it adds are merged with the ones that the Activity created.
Read everything to do with the options menu in this guide for more details:
http://developer.android.com/guide/topics/ui/menus.html
Is there a normal, object oriented way
Yes there is. It's called inheritance!
Create a base Activityin which you inflate the OptionsMenu once and extend this Activity for all your Activities.
public class BaseActivity extends Activity {
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.blabla:
// do something
break;
case R.id.blablab:
// do something
break;
}
return true;
}
}
Now you can do this:
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
return true;
}
}
I used a BaseActivity as Ahmad described. It works fine.
Now I want to show a special menu item in one specific Activity (which extends the BaseActivity).
#Override
public boolean onOptionsItemSelected(android.view.MenuItem item) {
switch (item.getItemId()) {
case 0:
Intent intent = new Intent(this, BlaActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem item = menu.add(Menu.NONE, 0, 0, "Edit");
item.setIcon(R.drawable.content_edit);
return super.onCreateOptionsMenu(menu);
}
This works as I expected, but I can't get the new inserted menuitem to get in the first position.
I need it to be in first position, so it is displayed directly in the ActionBar and the user sees that there is a new option without clicking on the options button.
Edit: I just found the solution myself.
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem item = menu.add(Menu.NONE, 0, 0, "Bearbeiten");
item.setIcon(R.drawable.content_edit);
if (android.os.Build.VERSION.SDK_INT >= 11) {
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); //<-----
}
return super.onCreateOptionsMenu(menu);
}
Related
I have a Sherlock Fragment Activity in which there are 3 Fragments.
Fragment A, Fragment B, Fragment C are three fragments. I want to show a done option menu in Fragment B only.
And the activity is started with Fragment A. When Fragment B is selected done button is added.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if(!menusInflated){
inflater.inflate(R.menu.security, menu);
menusInflated=true;
}
super.onCreateOptionsMenu(menu, inflater);
}
When I again start Fragment A I want to options Menu DONE (which was set at Fragment B) for this I am doing like this
setHasOptionsMenu(false);
MenuItem item = (MenuItem) menu.findItem(R.id.done_item);
item.setVisible(false);
But this is not hiding at all, also it is giving NullPointerException when Activity if first started with Fragment A.
Please let me know what is the problem.
Try this...
You don't need to override onCreateOptionsMenu() in your Fragment class again. Menu items visibility can be changed by overriding onPrepareOptionsMenu() method available in Fragment class.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.action_search).setVisible(false);
super.onPrepareOptionsMenu(menu);
}
This is one way of doing this:
add a "group" to your menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<group
android:id="#+id/main_menu_group">
<item android:id="#+id/done_item"
android:title="..."
android:icon="..."
android:showAsAction="..."/>
</group>
</menu>
then, add a
Menu menu;
variable to your activity and set it in your override of onCreateOptionsMenu:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
this.menu = menu;
// inflate your menu here
}
After, add and use this function to your activity when you'd like to show/hide the menu:
public void showOverflowMenu(boolean showMenu){
if(menu == null)
return;
menu.setGroupVisible(R.id.main_menu_group, showMenu);
}
I am not saying this is the best/only way, but it works well for me.
To show action items (action buttons) in the ActionBar of fragments where they are only needed, do this:
Lets say you want the save button to only show in the fragment where you accept input for items and not in the Fragment where you view a list of items, add this to the OnCreateOptionsMenu method of the Fragment where you view the items:
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (menu != null) {
menu.findItem(R.id.action_save_item).setVisible(false);
}
}
NOTE: For this to work, you need the onCreate() method in your Fragment (where you want to hide item button, the item view fragment in our example) and add setHasOptionsMenu(true) like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
Might not be the best option, but it works and it's simple.
This will work for sure I guess...
// Declare
Menu menu;
MenuItem menuDoneItem;
// Then in your onCreateOptionMenu() method write the following...
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
this.menu=menu;
inflater.inflate(R.menu.secutity, menu);
}
// In your onOptionItemSelected() method write the following...
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.done_item:
this.menuDoneItem=item;
someOperation();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// Now Making invisible any menu item...
public void menuInvisible(){
setHasOptionsMenu(true);// Take part in populating the action bar menu
menuDoneItem=(MenuItem)menu.findItem(R.id.done_item);
menuRefresh.setVisible(false); // make true to make the menu item visible.
}
//Use the above method whenever you need to make your menu item visible or invisiable
You can also refer this link for more details, it is a very useful one.
MenuItem Import = menu.findItem(R.id.Import);
Import.setVisible(false)
Try this
#Override
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.custom_actionbar, menu);
menu.setGroupVisible(...);
}
By setting the Visibility of all items in Menu, the appbar menu or overflow menu will be Hide automatically
Example
private Menu menu_change_language;
...
...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
...
...
menu_change_language = menu;
menu_change_language.findItem(R.id.menu_change_language).setVisible(true);
return super.onCreateOptionsMenu(menu);
}
Before going to other fragment use bellow code:
if(menu_change_language != null){
menu_change_language.findItem(R.id.menu_change_language)
.setVisible(false);
}
Hello I got the best solution of this, suppose if u have to hide a particular item at on create Menu method and show that item in other fragment. I am taking an example of two menu item one is edit and other is delete. e.g menu xml is as given below:
sell_menu.xml
<?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_edit"
android:icon="#drawable/ic_edit_white_shadow_24dp"
app:showAsAction="always"
android:title="Edit" />
<item
android:id="#+id/action_delete"
android:icon="#drawable/ic_delete_white_shadow_24dp"
app:showAsAction="always"
android:title="Delete" />
Now Override the two method in your activity & make a field variable mMenu as:
private Menu mMenu; // field variable
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.sell_menu, menu);
this.mMenu = menu;
menu.findItem(R.id.action_delete).setVisible(false);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_delete) {
// do action
return true;
} else if (item.getItemId() == R.id.action_edit) {
// do action
return true;
}
return super.onOptionsItemSelected(item);
}
Make two following method in your Activity & call them from fragment to hide and show your menu item. These method are as:
public void showDeleteImageOption(boolean status) {
if (menu != null) {
menu.findItem(R.id.action_delete).setVisible(status);
}
}
public void showEditImageOption(boolean status) {
if (menu != null) {
menu.findItem(R.id.action_edit).setVisible(status);
}
}
That's Solve from my side,I think this explanation will help you.
You can make a menu for each fragment, and a global variable that mark which fragment is in use now.
and check the value of the variable in onCreateOptionsMenu and inflate the correct menu
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (fragment_it == 6) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.custom_actionbar, menu);
}
}
Okay I spend couple of hour to get this solution.apparently you can get menuitem from your toolbar to anywhere in activity or fragment. So in my case.
var menuItem = toolbar.menu;
Now to get specfic item from menu item
favIcon = menuItem.findItem(R.id.favourite);
Note: favIcon is MenuItem declare global
Now if you can do whatever you want to do for this icon
eg. to make it invisible
favIcon?.isVisible=false
Even though the question is old and answered. There is a simpler answer to that than the above mentioned. You don't need to use any other variables.
You can create the buttons on action bar whatever the fragment you want, instead of doing the visibility stuff(show/hide).
Add the following in the fragment whatever u need the menu item.
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
Sample menu.xml file:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/action_addFlat"
android:icon="#drawable/add"
android:showAsAction="ifRoom|withText"
android:title="#string/action_addFlat"/>
</menu>
Handling onclick events is as usual.
Late to the party but the answers above didn't seem to work for me.
My first tab fragment (uses getChildFragmentManager() for inner tabs) has the menu to show a search icon and uses android.support.v7.widget.SearchView to search within the inner tab fragment but navigating to other tabs (which also have inner tabs using getChildFragmentManager()) would not remove the search icon (as not required) and therefore still accessible with no function, maybe as I am using the below (ie outer main tabs with each inner tabs)
getChildFragmentManager();
However I use the below in my fragments containing/using the getChildFragmentManager() for inner tabs.
//region onCreate
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
//access setHasOptionsMenu()
setHasOptionsMenu(true);
}
//endregion onCreate
and then clear the menu item inside onPrepareOptionsMenu for fragments(search icon & functions)
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
//clear the menu/hide the icon & disable the search access/function ...
//this will clear the menu entirely, so rewrite/draw the menu items after if needed
menu.clear();
}
Works well and navigating back to the tab/inner tab with the search icon functions re displays the search icon & functions.
Hope this helps...
For some reason the method was not working for me this is how I solved it according to the accepted solution
//This should be in on create
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
showOverflowMenu(false);
}
},100);
#Override
public boolean onCreateOptionsMenu(Menu menu) {
this.menu = menu;
getMenuInflater().inflate(R.menu.options_menu, menu);
return true;
}
public void showOverflowMenu(boolean showMenu){
if(menu == null)
return;
menu.setGroupVisible(R.id.grp, showMenu);
}
I'm using ActionBarSherlock in my project and sometimes need to add one or more items inside the action bar.
At this BaixadosFragment class (that extends SherlockFragment), I'm using the following code and it works fine:
#Override
public void onCreateOptionsMenu(Menu menu,MenuInflater inflater)
{
inflater.inflate(R.menu.main, menu);
super.onCreateOptionsMenu(menu, inflater);
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.refresh:
refresh();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
In this case, I'm adding a refresh button, witch is lonely inside main.xml
BUT I want to do the same at CupomDetalheActivity (though adding a share button), witch extends SherlockFragmentActivity instead. So I'm not able to override "onCreateOptionsMenu" as it has a different signature (below):
//this is inside SherlockFragmentActivity
public final boolean onCreateOptionsMenu(android.view.Menu menu) {
return true;
}
//this is inside SherlockFragment
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
//Nothing to see here.
}
Whith SherlockFragmentActivity, I don't even see where can I use the inflater to bring up the xml containing the share button...
I appreciate a lot any ideas and suggestions...
[EDIT] This worked, according to DroidT's suggestion:
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.share, menu);
super.onCreateOptionsMenu(menu);
return true;
}
Your SherlockFragmentActivity also has an onCreateOptionsMenu() and onPrepareOptionsMenu(). You can inflate your menu options in the onCreateOptionsMenu() by using getSupportMenuInflater(). You would want to make a call to invalidateOptionsMenu() in your SherlockFragmentActivity whenever you want the change to happen and add the menu options in onPrepareOptionsMenu(). For more information look at the "Changing menu items at runtime" section of this link.
If you are using a menu inside of a fragment, make sure you call setHasOptionsMenu(true); in the fragments onCreate(Bundle savedInstance) method
The Android design guide says that Help should always be placed as the last item of the overflow menu (it should never appear in the ActionBar) and also, that is should be present in every activity, so that users don't have to look for it. Similar approach is also recommended for Settings.
However, I'm wondering what is the best way to make sure that all the activities in my app handle these items without lots of code repetition? Putting these common items manually into every XML menu file and then manually handling clicks on each of them in every activity class is just nonsense.
Since I am already extending all of my activities from a common base class (which provides some convenience methods), I came up with this approach: In the BaseActivity class, I define an empty initOptionsMenu() method which the subclasses may override (template method pattern style) by adding their specific items to the menu. This method is called at the start of onCreateOptionsMenu() and then the base class adds the common items (settings and help) at the end of the menu.
The onOptionsItemSelected() method follows the standard pattern - it switches on the item ID and in the default case, it passes the handing to the superclass. Again, the base class handles the setting and help cases.
public class BaseActivity extends Activity {
#Override
public boolean onCreateOptionsMenu(Menu menu) {
initOptionsMenu(menu);
menu.add(Menu.NONE, R.id.menu_settings, Menu.NONE, R.string.menu_help);
menu.add(Menu.NONE, R.id.menu_help, Menu.NONE, R.string.menu_help);
return true;
}
protected void initOptionsMenu(Menu menu) {}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_settings:
startActivity(new Intent(this, SettingsActivity.class));
return true;
case R.id.menu_help:
startActivity(new Intent(this, HelpActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
Now, whenever I create a new activity, I extend the BaseActivity. If the derived activity provides some more items for the options menu, I do not override the standard onOptionsItemSelected(), but instead I override the custom initOptionsMenu() method and populate the menu there. In onOptionsItemSelected(), I need to handle only cases for the items specific for this activity, the common ones will be handled by the super call.
public class FooActivity extends BaseActivity {
#Override
protected void initOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.foo, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// cases for items in R.menu.foo
default:
return super.onOptionsItemSelected(item);
}
}
}
Is this pattern sensible? Can you come up with a better approach? Please share your thoughts.
I might not use initOptionsMenu method. I will just call super.onCreateOptionsMenu() after adding my menu from concrete implementation. My BaseActivity will add settings and help menu so
in BaseActivity:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, R.id.menu_settings, Menu.NONE, R.string.menu_help);
menu.add(Menu.NONE, R.id.menu_help, Menu.NONE, R.string.menu_help);
return true;
}
and in MainActivity extends BaseActivity:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, R.id.menu_create_dummy, Menu.NONE, R.string.menu_dummy);
menu.add(Menu.NONE, R.id.menu_delete_dummy, Menu.NONE, R.string.menu_dummy);
return super.onCreateOptionMenu(menu);
}
I have the following in my activity (sorry new to Java/Android):
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.selectItem:
// menu.add(...) --> how to get the menu instance?
return true;
default:
return super.onOptionsItemSelected(item);
}
}
I am wondering, how can I access a menu object in onOptionsItemSelected? For example, how would I go about adding a new view to the options menu based on the selection of an existing menu item? Is the answer related to "onPrepareOptionsMenu"?
you should use SubMenu for such things ... remeber that you cant add submenu to another submenu ... so only Menu->Submenu is possible you can't do stuff like this Menu->Submenu->Submenu (while Submenu is Dialog with choices)
I need to enable a MenuItem when a previous screen (Activity) returns.
I tried this code:
...
((MenuItem)findViewById(R.id.menu_how)).setEnabled(true);
...
but a null pointer exception is launched.
BTW, the menu_how is set to false in xml; and the code is part of onActivityResult(int requestCode, int resultCode, Intent data) call.
Try using menu.findItem(R.id.menu_how) in onCreateOptionsMenu and save a reference for later use.
This should work fine with enabled, however, I've found that setting a menu item to invisible in the XML means you can't show/hide it programmatically.
I found something at the android dev site that might be helpful (look for the section "Changing menu items at runtime")
It said that the onCreateOptionsMenu() method fired only when the the menu for the activity is created, and it happens when this activity starts. So if you want to change the menu items after the menu/activity was created, you need to override the onPrepareOptionsMenu() method instead. search the link for full details.
EDIT:
Just made it and it's working fine. I'm using one boolean var per menuItem which represents if this item should be enabled or not. This is my code:
/*************************************Game Menu**************************************/
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.game_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId())
{
case R.id.gm_new_game:
//newGame();
return true;
case R.id.gm_stand_up:
//some code when "gm_stand_up" button clicked..
return true;
case R.id.gm_forfeit:
//some code when "gm_forfeit" button clicked..
return true;
case R.id.gm_surrender:
//some code when "gm_surrender" button clicked..
return true;
case R.id.gm_exit_table:
exitTableCommand();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public boolean onPrepareOptionsMenu(Menu menu)
{
menu.findItem(R.id.gm_forfeit).setEnabled(forfeitMenuButtonIsEnabled);
menu.findItem(R.id.gm_surrender).setEnabled(surrenderMenuButtonIsEnabled);
menu.findItem(R.id.gm_new_game).setEnabled(newGameMenuButtonIsEnabled);
menu.findItem(R.id.gm_stand_up).setEnabled(standUpMenuButtonIsEnabled);
return super.onPrepareOptionsMenu(menu);
}
where are you calling this? (Sorry, didn't read carefully) I think you need to call it after the menu is inflated (usually in OnCreateOptionsMenu). To do this, you can set a variable to true when the other Activity returns, then do ((MenuItem)findViewById(R.id.menu_how)).setEnabled(mMyBooleanField) in OnCreateOptionsMenu after the call to inflater.inflate.
Edit: To accomplish this in code, it might look something like this:
At the top of the class (along with all the other class members):
Boolean mEnableMenuItem = false;
In OnCreateOptionsMenu:
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_main, menu);
((MenuItem)findViewById(R.id.menu_how)).setEnabled(mEnableMenuItem );
In OnActivityResult:
mEnableMenuItem = true;
Keep a reference to Menu in your activity:
private Menu mMenu;
Then:
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_note, menu);
mMenu = menu;
return true;
}
Now, to access menu items anywhere in your activity use similar code to this:
mMenu.findItem(R.id.menu_how).setVisible(false);
or
mMenu.findItem(R.id.menu_how).setEnabled(true);