In Xamarin the OnOptionsItemSelected is not called - android

I read different articles here about OnOptionsItemSelected not been called in Android, because I have the same problem in Xamarin.Android.
I have an menu in my main activity, but I haven't used fragments at all. The menu is read from an XML file:
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.main, menu);
return base.OnCreateOptionsMenu(menu);
}
And then I tried to catch the ItemSelected Events by using the following code:
public virtual bool OnOptionsItemSelected (IMenuItem item)
{
string test;
Console.WriteLine ("Test");
return true;
}
But it doesn't work and the event is not called.
I tried other events like:
public virtual bool OnMenuItemSelected (int featureId, IMenuItem item)
{
string test;
Console.WriteLine ("Test");
return true;
}
public void OnGroupItemClick( IMenuItem item) {
// One of the group items (using the onClick attribute) was clicked
// The item parameter passed here indicates which item it is
// All other menu item clicks are handled by onOptionsItemSelected()
int i = 0;
string test;
Console.WriteLine ("Test");
test = item.MenuInfo.ToString ();
}
But none of these events have been called. The menu is there, but I cannot catch the ClickedEvents.
How can I catch the ClickedEvents?

Your code seems to work in a clean application (just tried on Xamarin.Android Alpha channel, latest version, on VS2013).
A few things you might try:
Disable Fast Deployment in the project properties under Android Options
Delete the bin and obj folders from your project folder
Besides this, are you using any external library/component?

I have found the answer. The problem was that I was using virtual methods.
Instead of that I had to use override methods.

Related

What is onCreateOptionsMenu(Menu menu)

What are the two parameters Menu and menu in method onCreateOptionsMenu(Menu menu) and how to use this method.
I have another question why this parameter is used in
Intent intent = new Intent(this, DisplayMessageActivity.class);
Menu is just the type of the parameter menu. For example you can have a String type for a variable named string, dog, etc. And in this case there's a Menu type for a parameter named menu.
You use onCreateOptionsMenu() to specify the options menu for an activity.
In this method, you can inflate your menu resource (defined in XML) into the Menu provided in the callback.
For example:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.game_menu, menu);
return true;
}
Fore more information, visit this link.
As for this,
Within an instance method or a constructor, this is a reference to the current object — the object whose method or constructor is being called.
For example:
public void sendMessage() {
Intent intent = new Intent(this, DisplayMessageActivity.class);
}
The constructor takes two parameters and a Context as its first parameter.
this represents environment data and provides global information about an application environment.
For more information on the intent example you provided, check this out.
The intent of implementing this method is to populate the menu passed with the items you define in the R.menu.game_menu layout file.
#Java
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.game_menu, menu);
return true;
}
#Kotlin
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.game_menu, menu)
return true
}
After inflating the menu with the items you might want to add some action when they are selected:
Java
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item:
// Action goes here
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Kotlin
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_item -> {
// Action goes here
true
}
else -> super.onOptionsItemSelected(item)
}
}
When onCreateOptionsMenu is called?
onCreateOptionsMenu() is called by the Android runtime when it need to create the option menu.
Android Developer Guide: Menus
If you've developed your application for Android 2.3.x and lower, the system calls onCreateOptionsMenu() to create the options menu when the user opens the menu for the first time. If you've developed for Android 3.0 and higher, the system calls onCreateOptionsMenu() when starting the activity, in order to show items to the app bar.
How to build an option menu?
Please refer to other anwsers.
Why onCreateOptionsMenu return Boolean
Activity.html#onCreateOptionsMenu
You must return true for the menu to be displayed; if you return false it will not be shown.
Why onOptionsItemSelected return Boolean
When you successfully handle a menu item, return true. If you don't handle the menu item, you should call the superclass implementation of onOptionsItemSelected() (the default implementation returns false).

Xamarin Android Menu Bar repeats iteslf

I have a Xamarin Android project that contains a menu bar. I'm almost certain this was working correctly yesterday, but since then, every time I click on a menu bar item such as 'settings', which takes me to the Settings Activity, then go back to the Main activity, the menu bar items are repeating themselves.
So where i just had 'Settings' and 'help', i not have 2 settings, and 2 help items. If i do it again, i will have 3 of each.
I'm assuming this is something to do with the onPause() and onResume() methods as the app goes to another activity. But i can't see where i'm going wrong (i'm new to Android)
My code for generating the menu bar in the Main Activity is:
public override bool OnPrepareOptionsMenu(IMenu menu1)
{
MenuInflater.Inflate(Resource.Menu.myMenuBar, menu1);
return base.OnPrepareOptionsMenu(menu1);
}
public override bool OnOptionsItemSelected(IMenuItem item) //do something when an options item is pressed
{
switch (item.ItemId)
{
case Resource.Id.settingsItem:
showSettings ();
return true;
case Resource.Id.helpItem:
//do something
return true;
}
return base.OnOptionsItemSelected(item);
}
Any ideas on this would be appreciated. I'm sure it's something fairly simple but I don't know what.
You should probably have to clear the menu
public override bool OnPrepareOptionsMenu(IMenu menu1)
{
menu1.clear();
MenuInflater.Inflate(Resource.Menu.myMenuBar, menu1);
return base.OnPrepareOptionsMenu(menu1);
}
And perhaps you should inflate the menu in the OnCreateOptionsMenu(IMenu, MenuInflater) method. OnPrepareOptionsMenu(IMenu) is meant for enabling/disabling and dynamically modifying items.

Android - Put interface in Bundle

I'm working with ActionBarSherlock, I need to change some icons on the action bar when the user does some stuff. In order to that I have to save the action bar for later usage :
private Menu actionBarMenu;
...
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.map_activity_menu, menu);
actionBarMenu = menu;
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
actionBarMenu.findItem(R.id.follow_my_position).setIcon(R.drawable.toolbar_myposition_active);
}
Okay ! Here's where the problems begin. When I rotate the screen, I get a NullPointerException on actionBarMenu.
I know... I didn't save it before the screen was rotated, so normally I would go to onSaveInstanceState and save my Menu there, except the actionBarMenu is an interface... More specifically, the interface com.actionbarsherlock.view.Menu and I can't modify it so that it implements Parcelable or Serializable.
So how can I save an interface in the bundle ?
You should not keep reference to your action bar during configuration changes. After screen rotate, Android will recreate your activity and all UI references should be released, otherwise you will introduce reference leak.
Why dont you get your actionBarMenu reference again inside boolean onCreateOptionsMenu, after activity rotates?
You can't. However, you can still retain your member variable on an orientation change by adding the following code to your activity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Object last = getLastNonConfigurationInstance();
if (last != null && last instanceof Menu) {
actionBarMenu = (Menu) last;
}
}
// for FragmentActivity it is onRetainCustomNonConfigurationInstance
public Object onRetainNonConfigurationInstance() {
return actionBarMenu;
};
You don't. You don't save the interface.
You save some String, or boolean, or integer, representing the action the user did.
Then you check this value and change the menu again.
Does this work before the rotate? Because a rotation just recreates the entire activity - i.e. it goes through the whole creation process again. So, I'm not sure that the recreation is your problem, because onCreateOptionsMenu(Menu menu); should be called again, anyway.
Are you sure that your actionBarMenu.findItem(R.id.follow_my_position) is returning correctly? Instead of handling it how your currently are - why not just...not store the menu and check the clicked menu item instead?
For example:
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId()){
case R.id.follow_my_position:
item.setIcon(R.drawable.toolbar_myposition_active);
}
}
or if that's not what you're looking for, check that your findItem() call finds the item:
MenuItem item = actionBarMenu.findItem(R.id.follow_my_position);
if(item != null){
item.setIcon(R.drawable.toolbar_myposition_active);
}

How to remove items from contextual action bar android

I want to remove COPY, SELECT ALL and FIND from android contextual action bar and add custom menu items.
This is appearing while selecting text on webview. I am trying to add Text Highlights on webview using js.
In order to accomplish what you want, you will need to create an entirely new contextual action bar. This is done through creating a custom ActionMode. Within your WebView, create a nested class that implements ActionMode.Callback. You can use this as a template:
public class CustomWebView extends WebView {
private ActionMode.Callback mActionModeCallback;
#Override
public ActionMode startActionMode(ActionMode mode) {
// This block is directly from the WebView source code.
ViewParent parent = getParent();
if (parent == null) {
return null;
}
mActionModeCallback = new CustomActionModeCallback();
return parent.startActionModeForChild(this, mActionModeCallback);
}
private class CustomActionModeCallback implements ActionMode.Callback {
// Called when the action mode is created; startActionMode() was called
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
return true;
}
// Called each time the action mode is shown.
// Always called after onCreateActionMode, but
// may be called multiple times if the mode is invalidated.
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// This method is called when the selection handlebars are moved.
return false; // Return false if nothing is done
}
// Called when the user selects a contextual menu item
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_button_1:
// Do stuff
break;
case R.id.menu_button_2:
// Do stuff
break;
default:
// You did not handle the action, so return false
// If you have implemented a case for every button,
// this block should never be called.
return false;
}
// If you want to close the CAB immediately after
// picking an action, call mode.finish().
// If you want the CAB to persist until the user clears the selection
// or clicks the "Done" button, simply return true.
mode.finish(); // Action picked, so close the CAB
return true;
}
// Called when the user exits the action mode
#Override
public void onDestroyActionMode(ActionMode mode) {
mode = null;
}
}
}
Be sure to define a menu in your XML resources. Here is an example to go with the above template:
<!-- context_menu.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/menu_button_1"
android:icon="#android:drawable/menu_button_1"
android:showAsAction="always"
android:title="#string/menu_button_1">
</item>
<item
android:id="#+id/menu_button_2"
android:icon="#drawable/menu_button_2"
android:showAsAction="ifRoom"
android:title="#string/menu_button_2">
</item>
</menu>
I noticed you did not explicitly state that you want to replace SHARE and WEB SEARCH. Unfortunately, this method does require you to implement all functionality on your own. However, you can dig into the source code (I would start in ActionMode.java) for those functions. Implement a new case in CustomActionModeCallback.onActionItemClicked (where you handle your button events), copy/paste the functionality from source, and add a corresponding button in your XML file. You can even use the native icon for those functions: android:icon="#android:drawable/[name_of_desired_icon]
For reference, this information is from the Android Developers website.
http://developer.android.com/guide/topics/ui/menus.html#CAB
May be this helps you and also stack members...
https://github.com/btate/BTAndroidWebViewSelection

How to handle onContextItemSelected in a multi fragment activity?

I'm currently trying to adapt my application to use the "Compatibility Libraries for Android v4" to provide the benefits of the usage of fragments even to Android 1.6 users.
The implementation of a context menu seems to be tricky:
The main activity of the application
is extending the FragmentActivity
class.
The fragments are all based on one
class which extends the Fragment class.
The fragment class is calling
registerForContextMenu() in its onCreateView() method and overrides the methods
onCreateContextMenu() and onContextItemSelected().
For onCreateContextMenu() this works pretty well. The context menu is inflated from a resource file and slightly modified based on the selected item (which is based on a listView... even if the fragment is not an ListFragment).
The problem occurs when a context menu entry is selected.
onContextItemSelected() is called for all currently existing fragments starting with the first added one.
In my case the fragments are used to show the content of a folder structure. When the context menu of a subfolder fragment is opened and a menu item is selected, onContextItemSelected() is first called on the upper levels (depending on how many fragments are allowed/visible in this moment).
Right now, I use a workaround by a field on activity level which holds the tag of last fragment calling its onCreateContextMenu(). This way I can call "return super.onContextItemSelected(item)" in the begin of onContextItemSelected() when the stored tag is not the same as getTag().
But this approach looks a bit dirty to me.
Why is onContextItemSelected() called on all fragments? and not just one the one that was calling onCreateContextMenu()?
What is the most elegant way to handle this?
I'll post an answer even though you found a workaround because I just dealt with a similar issue. When you inflate the context menu for a specific fragment, assign each menu item a groupId that is unique to the fragment. Then test for the groupId in 'onContextItemSelected.' For Example:
public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_1, 0, R.string.src1);
menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_2, 0, R.string.src2);
}
public boolean onContextItemSelected(MenuItem item) {
//only this fragment's context menus have group ID of -1
if (item.getGroupId() == UNIQUE_FRAGMENT_GROUP_ID) {
switch(item.getItemId()) {
case MENU_OPTION_1: doSomething(); break;
case MENU_OPTION_2: doSomethingElse(); break;
}
}
This way all of your fragments will still receive calls to 'onContextItemSelected,' but only the correct one will respond, thus avoiding the need to write activity-level code. I assume a modified version of this technique could work even though you aren't using 'menu.add(...)'
Another one solution:
#Override
public boolean onContextItemSelected(MenuItem item) {
if (getUserVisibleHint()) {
// context menu logic
return true;
}
return false;
}
Based upon this patch from Jake Wharton.
I liked the simple solution by Sergei G (based on Jake Wharton fix), but inverted because it is easier to add to several fragments:
public boolean onContextItemSelected(android.view.MenuItem item)
{
if( getUserVisibleHint() == false )
{
return false;
}
// The rest of your onConextItemSelect code
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
}
After that, the code same as it was before.
I found out a very easy solution. As onCreateContextMenu() is called every time the ContextMenu is created I set a boolean variable to true.
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.film_menu, menu);
bMenu=true;
}
The only other thing I have to do is ask for that variable OnContextItemSelected()
public boolean onContextItemSelected(MenuItem item) {
if (bMenu) {
bMenu=false;
if (item.getItemId() == R.id.filmProperties) {
///Your code
return true;
} else {
return super.onContextItemSelected(item);
}
} else {
return super.onContextItemSelected(item);
}
}
That's it.
I found an alternative. It does not change anything on my problem above, but it makes it pointless.
I have remove the context menu completely from my application. Instead I capture the longclick on a list item and change the visible buttons of the action bar in this moment.
From the user point of view this is much more tablet like as a context menu.
In backward compatible applications the actionbar does not exist. So I've decided to build my own (kind of toolbar on top) for the devices pre Honeycomb.
If you would like to stay with the context menu, I did not find a better solution as the workaround I've mentioned above.
In my first fragment, i have set all my menu id > 5000 so, as first line of code of onContextItemSelected of first fragment i have
if (item.getItemId() < 5000) return false;
and the second fragment will be invoked.
If you are using adapters with listviews in your fragment this might help.
public boolean onContextItemSelected(final MenuItem item) {
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
//Check if the context menu call came from the list in this fragment (needed for support for multiple fragments in one screen)
if (info.targetView.getParent() != getView().findViewById(android.R.id.list))
return super.onContextItemSelected(item);
//Handle context menu item call
switch (item.getItemId()) {
...
}
}
Just change
#Override
public boolean onContextItemSelected(MenuItem item) {
return true;
}
to
#Override
public boolean onContextItemSelected(MenuItem item) {
return super.onContextItemSelected(item);
}
and will work great!!!
IMHO we may just check if target view is child of fragment listview. It is very simple and work for me well. I just added to all my fragments:if (getListView.getPositionForView(info.targetView) == -1)
return false when migrate from older API
This is example from one of my parent fragments. This is Scala, but I hope you got an idea.
#Loggable
override def onContextItemSelected(menuItem: MenuItem): Boolean = {
for {
filterBlock <- TabContent.filterBlock
optionBlock <- TabContent.optionBlock
environmentBlock <- TabContent.environmentBlock
componentBlock <- TabContent.componentBlock
} yield menuItem.getMenuInfo match {
case info: AdapterContextMenuInfo =>
if (getListView.getPositionForView(info.targetView) == -1)
return false
TabContent.adapter.getItem(info.position) match {
case item: FilterBlock.Item =>
filterBlock.onContextItemSelected(menuItem, item)
case item: OptionBlock.Item =>
optionBlock.onContextItemSelected(menuItem, item)
case item: EnvironmentBlock.Item =>
environmentBlock.onContextItemSelected(menuItem, item)
case item: ComponentBlock.Item =>
componentBlock.onContextItemSelected(menuItem, item)
case item =>
log.debug("skip unknown context menu item " + info.targetView)
false
}
case info =>
log.fatal("unsupported menu info " + info)
false
}
} getOrElse false
P.S. If you trace calls of onContextItemSelected(...) you may notify that super.onContextItemSelected(item) return always false. Valid onContextItemSelected invoked AFTER, not WITHIN. So super.onContextItemSelected(item) is useless and I replaced it with false.
I found an easier solution than the exposed:
public boolean onContextItemSelected(MenuItem item) {
ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList);
if (!yourList.hasFocus())
return false;
switch(item.getItemId()) {
...
}
}
In the method changed return true; to return super.onContextItemSelected(item); in my onContextItemSelected() override and everything started working.

Categories

Resources