I use some custom ActionModes in my application. When an action mode is closed, I do some housekeeping, like closing related views, updating changes, etc.. I detect the action mode has been closed in OnDestroyActionMode.
My problem is, when inside of some of my ActionModes, the user may trigger another system actionmode (The text copy/paste/select). In that case, onDestroyActionMode is called and I erroneously asume the user is done with the first actionmode, rather than implement a "stack" functionality, so can I ignore this onDestroyActionMode, let the user edit / cut / etc the text, then reopen the former actionmode when done.
How can I achieve this?
Shedding further light on your situation: prior to honeycomb, longPress on a TextView will yield a popup window with options (like 'Select word', 'Select all', and 'Add "someword" to dictionary') while NOT affecting any existing ActionMode both when shown and when dismissed (by pressing back). So this isn't really a problem pre-honeycomb.
More light regarding HTC Sense: Sense does NOT honour TextView.setCustomSelectionActionModeCallback() because Sense doesn't use an ActionMode for the Text selection feature (and clearly don't care if the rest of the world do!). So this problem has a different smell in that situation (I haven't tested the following solution under Sense, so not sure how it'll behave).
A solution is to create your own custom ActionMode.Callback to replace the OS's one and apply it in setCustomSelectionActionModeCallback() of any TextView and/or EditText you desire (though only if device is running honeycomb or greater). Pass a custom onTextSelectionCABDestroyed callback interface to your custom ActionMode.Callback, call it in the onDestroyActionMode method.
Firstly create an interface and implement it where you want to handle the recreation of your original ActionMode (alternatively you may want to use a bus event with something like Otto):
public interface YourCallbackInterface {
public void onTextSelectionCABDestroyed();
}
and create a new class:
public final class CustomTextSelectionActionModeCallback implements ActionMode.Callback {
WeakReference<YourCallbackinterface> mYourCallbackinterface;
public CustomTextSelectionActionModeCallback(YourCallbackinterface yourCallbackInterface) {
mYourCallbackinterface = new WeakReference<YourCallbackinterface>(yourCallbackInterface);
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return true; //returning true will create the ActionMode
}
#Override
public void onDestroyActionMode(ActionMode mode) {
//this is the magic where we actually capture the destroy event for TextSelectionCAB and can subsequently do things like recreate the ActionMore that TextSelectionCAB greedily destroyed!
mYourCallbackinterface.get().onTextSelectionCABDestroyed();
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
}
And remember to avoid StackOverflowException when recreating an ActionMode from the onDestroyActionMode of an ActionMode, postDelayed a Runnable to a Handler like this I explain here: Reopen ActionMode (or CAB) after onDestroyActionMode is called
Lastly, if you're using ActionBarSherlock, make sure that your CustomTextSelectionActionModeCallback implements android.view.ActionMode.Callback rather than com.actionbarsherlock.view.ActionMode.Callback.
Note: I haven't played with ActionBarCompat so not sure how all this applies there. If someone knows, please post as comment!
Related
When you create a button, you can assign a listener to capture user actions like clicks.
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//do some stuff
}
});
}
My question is why menu items don't need a listener? All I need is just write
getMenuInflater().inflate(R.menu.main, menu);
inside of
public boolean onCreateOptionsMenu(Menu menu).
and use
public boolean onOptionsItemSelected(MenuItem item)
to define behaviour when an Item is clicked. I didn't see any setListener method. Besides, the main activity class does not implement any Listener. How does a menuItem work?
How does a menuItem work?
So OptionsMenu is build-in widget in Android OS
It works simply said like when you'll click on an item then is immediately called "OnItemSelectedListener" (you don't need to implement it, it's automatic called whenever you'll click on the item). This listener is already implemented. You don't have to implement it and i think you shouldn't and can't.
An onOptionsItemSelected method is called by listener that is called when you'll click on the item.
And what docs exactly say:
This hook is called whenever an item in your options menu is selected.
The default implementation simply returns false to have the normal
processing happen (calling the item's Runnable or sending a message to
its Handler as appropriate). You can use this method for any items for
which you would like to do processing without those other facilities.
Derived classes should call through to the base class for it to
perform the default menu handling.
I see that it's possible to handle a tap on a icon menù item or by implementing
onOptionsItemSelected
inside the acivity, or by using
onMenuItemClickListener
like onclick listener on a button. When is better to use the fist one method, and when the second one?
Because for my opinion, using an external listener makes more modular the code, but create a new class, but using the first way don't create new class, but makes code less modular...
There are use cases other than the ones outlined below, but I'm putting in the general cases that come up regularly.
onOptionsItemSelected
If you're using Fragments, you may want to use onOptionsItemSelected and consider adding menu items to the Action Bar the way that is described in Adding items to the Action Bar.
What this describes is implementing onCreateOptionsMenu inside your Fragment. To make this happen, you must call setHasOptionsMenu in onCreate.
protected void onCreate(Bundle savedInstanceState) {
this.setHasOptionsMenu(true);
}
Setting this will actually make the Activity call onCreateOptionsMenu which allows you to add the menu items.
#Override
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
// add items corresponding to this Fragment
menu.add(...);
return true;
}
The reason I recommend this is that it allows you to put more of the menu handling code into your Fragment instead of the Activity to figure out which Fragment to call, etc.
In this case, clicking the menu item will call onOptionsItemSelected inside of your Fragment which I suggest.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.my_id1:
dothing1();
return true;
case R.id.my_id2:
dotghing2();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
More of a long winded answer, but this is the way to handle menu clicks inside your Fragment.
onMenuItemClickListener
In the case of onMenuItemClickListener, this is used when you DON'T want to use the pre-ready method above and implement your own.
What I mean by that is you implement OnMenuItemClickListener and generate the methods in the interface. You then assign the menu to call the Activity that implemented this where as the above option assumes what Activity to use based on the pre-ready implementation of the Activity to Fragment relationship.
If you are targeting API 14 or greater (ICS or above) you could implement an ActionProvider. If that's not an option then you could implement a base activity that will always populate the menu and handle any menu clicks using onOptionsItemSelected. This is a good approach to implement "About" or "Settings" menu items through all your activities.
Our UX asks for a button to start multi-choice mode. this would do the same thing as long-pressing on an item, but would have nothing selected initially.
What I'm seeing in the code is that I cannot enter multi-choice mode mode unless I have something selected, and if I unselect that item, multi-choice mode exits (contextual action bar closes).
I've also tried this in other apps (gmail), and it works the same way.
Is there a way to be in multi-select mode, with no items selected?
It's very hacky, but I've done this by having an item selected, but making it look like it's not selected, by making the background temporarily transparent. When an item is then selected by the user, the secretly-selected item is deselected and the background restored to normal. Or, if it's the secretly-selected item which is selected (thus deselecting it), I reselect it, then set a boolean to stop it happening again.
I also had to use a counter in onItemCheckedStateChanged, as I was changing the checked state of the secret item from within that callback, resulting in a loop.
Probably not an ideal solution for all cases, but I don't think there's another way to do it at the moment, since AbsListView can't easily be extended.
Edit: if the screen orientation changes while the selected state of the selected item is hidden, it will suddenly be shown as being selected, so you have to make sure to save the fact that it should be hidden, and restore it after the listview is recreated. I had to use the View post() method to ensure the restoration happened after the listview had finished redrawing all its child items after the configuration change.
Edit: another potential issue is if the user tries to carry out an action while there are supposedly no items selected. As far as the application knows there is an item selected so it will carry out the action on that item, unless you make sure it doesn't.
Just call:
mListView.setItemChecked(-1, true);
ListView's actionMode will be started without selecting any list element.
Make sure you've properly set your ListView before call:
mListView.setMultiChoiceModeListener( ... )
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
or
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
You just have to use :
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
If you want to change the action bar, call this from your activity:
startActionMode(new ActionMode.Callback {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
});
Halo,
My ideia is to show a Context Menu by clicking on one of the options menu.
I don't think it will be quite difficult but I'm not being able to get the View in the OptionsMenu class.
So actually it is just call the showContextMenu() on the OptionsMenu class.
Can somebody give me a hint?
ty!
It's possible to call a Context Menu by means other than a long press. If you implement your menu options within your activity, using an inner class for instance, you should be able to call your context menu:
yourView.showContextMenu();
Remember to register your context menu at onCreate:
registerForContextMenu(yourView);
Finally made it, and it was a quite simple solution.
Basically I pass the View, to the OptionsMenu constructor.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_event);
View v = findViewById(R.id.activity_event);
registerForContextMenu(v);
options = new Options(getApplicationContext(), getMenuInflater(), v);
fillData();
}
...
private class Options extends ActivityOptionsMenu {
public Options(Context c, MenuInflater mi, View v) {
super(c, mi, v);
}
}
...
private abstract class ActivityOptionsMenu {
...
public void onMenuItemSelected(int featureId, MenuItem item) {
switch (item.getItemId()) {
case R.id.directions:
v.showContextMenu();
}
}
}
Thanks for all the help.
My ideia is to show a Context Menu by clicking on one of the options menu.
AFAIK, you cannot show a context menu from a long-tap on an option menu choice.
I don't think it will be quite difficult but I'm not being able to get the View in the OptionsMenu class.
The View is not exposed to you.
The only way to literally do what you seek is to not use the standard Android option menu. You can watch for the MENU key via onKeyDown() in your activity, at which point you can display whatever you want, including something that supports context menus.
However, context menus are not particularly popular among users, simply because they are not very discoverable. Users never read the documentation, even if it is supplied, and they tend not to randomly stab the screen to see if a menu will pop up. They will be even less likely to decide to hold down a fake option menu choice to see if a context menu will pop up.
Hence, I really recommend you consider some other approach, for improved usability.
I have an Options menu up and running in my Android application and I've overridden the onCreateOptionsMenu, onOptionsItemSelected and onPrepareOptionsMenu methods to customize the menu a little.
My question is related to keeping the Options menu open after the user clicks on a menu item. Basically, I'd like to be able to hide the menu until the user clicks on the device menu key. Once the user clicks on this key, I'd like to be able to hold the menu in place regardless of how many times the user clicks on menu items. If the user wants to hide the Options menu, they'd just need to click on the device menu key again.
Is this type of interaction supported (or even advisable). If this interaction is not supported, any alternative suggestions are more than welcome.
Cheers!
Sean
This will not be possible with onCreateOptionsMenu and the other methods. They always act that way.
But you can do it another way. But there you have to program the whole menu yourself. Basically add the Menu in your layout.xml and let it be hidden (visibility = gone). Then you overwrite the methods onKeyDown. There you check if it is the Menu key. if the menu is not yet open yes, then you show the menu. If it is open already, you hide it.
should not be too difficult. Good thing as well is, that you can make the menu look exactly the way you want and as well let it react the way you want.
For anybody like me, who found this question in google:
To keep menu open after selecting item, you need this code:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
item.setChecked(!item.isChecked());
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
item.setActionView(new View(this));
item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
return false;
}
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
return false;
}
});
return false;
}
Important to return false in onOptionsItemSelected and methods of OnActionExpandListener
This solution from #MayurGajra. More details here: How to hold the overflow menu after I click it?