In Android, onContextItemSelected has a single MenuItem argument and so it isn't clear how to identify the view selected. MenuItem.getMenuInfo provides access to Contextmenu.ContextMenuInfo, but while both known subclasses provide access to the target view, there does not appear to be an accessor on the interface.
One alternative is to save the View provided in onCreateContextMenu in a private class variable which relies on onCreateContextMenu not being called again in the activity before onContextItemSelected. Another is to use the id of the View for the itemId argument of ContextMenu.add. If we do this, we would then need to identify the option selected from the context menu by using its (possibly internationalised) title.
What is the best method for identifying the View selected in onContextSelected?
There is no such concept as "identifying the View selected" for either option menus or context menus in Android. Hence, it is rather difficult to answer your question. So, I'll take some guesses.
If by "identifying the View selected" you mean which menu choice was selected, that is getItemId() on the MenuItem that is passed to onOptionsItemSelected() or onContextItemSelected().
If by "identifying the View selected" you mean which row in a ListView was the one long-tapped on to bring up the context menu, cast getMenuInfo() (called on the MenuItem) to AdapterView.AdapterContextMenuInfo, then use either the id or the position values as appropriate based on your adapter. See here for a sample project that uses this technique.
If by "identifying the View selected" you mean you have more than one non-ListView context menu in an activity, I would not use that UI technique.
The whole point of a context menu is that it is associated with an individual underlying view, and it is clearly a design limitation in Android that the association is lost in the callback 'onContextItemSelected'. Enabling long-touch on any view of sufficient size seems perfectly reasonable as an alternative to a right mouse click.
As other posts have recommended, for some contexts:
AdapterView.AdapterContextMenuInfo menuInfo =
(AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
is appropriate and the targetView is a useful reference point.
Another way is to subclass the view and override 'getContextMenuInfo' to provide the view reference. For example, a simple TextView:
package ...;
public class TextViewWithContext extends TextView {
TextViewContextMenuInfo _contextMenuInfo = null;
public TextViewWithContext(Context context) {
super(context);
_contextMenuInfo = new TextViewContextMenuInfo(this);
}
public TextViewWithContext(Context context, AttributeSet attrs) {
super(context, attrs);
_contextMenuInfo = new TextViewContextMenuInfo(this);
}
protected ContextMenuInfo getContextMenuInfo() {
return _contextMenuInfo;
}
public boolean isContextView(ContextMenuInfo menuInfo) {
return menuInfo == (ContextMenuInfo)_contextMenuInfo;
}
protected class TextViewContextMenuInfo implements ContextMenuInfo {
protected TextView _textView = null;
protected TextViewContextMenuInfo(TextView textView) {
_textView = textView;
}
}
}
...
#Override
public boolean onContextItemSelected(MenuItem item) {
ContextMenuInfo menuInfo = item.getMenuInfo();
if (textViewWithContext.isContextView(menuInfo) {
...
}
}
Finally, it would have been more helpful if the base View class had assigned a ContextInfo object with a reverse reference to the view, rather than null as at present.
class TestActivity extends Activity {
// create temp item here
private ImageView tmpImageView = null;
...
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo){
super.onCreateContextMenu(menu, v, menuInfo);
// initialize temp item
mCurrentStatusImage = (ImageView) v.findViewById(R.id.rule_status);
}
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case ENABLE_ID:
// use temp item
tmpImageView.setImageResource(android.R.drawable.presence_online);
return super.onContextItemSelected(item);
case DISABLE_ID:
// use temp item
tmpImageView.setImageResource(android.R.drawable.presence_invisible);
return super.onContextItemSelected(item);
default:
return super.onContextItemSelected(item);
}
I fixed a similar problem by setting a groupID for the MenuItem based on which item sent it e.g:
textview.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
#Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
menu.setHeaderTitle("Context Menu");
menu.add(R.id.whateverviewclicked, RENAME_MENU_ITEM, 0, "Rename");
menu.add(R.id.whateverviewclicked, DELETE_MENU_ITEM, 1, "Delete");
}
});
This would allow you to get the groupID in the onContextItemSelected:
public boolean onContextItemSelected(MenuItem aItem) {
int selectedViewID = aItem.getGroupId();
int selectedItem = aItem.getItemId();
};
you don't have to use the resource ID - you can use any int you want. Works for me!
In case you are attaching ContextMenus to multiple Views which are NOT in a ListView (that is there is no adapter underlying the Views) and you wish to determine which View was long-pressed to access the ContextMenu the following "hack" can be implemented. (It would be better if Android provided a listener that could be associated with each item).
The "hack" is that one creates a private View member mLastViewTouched in the class and then attach the following onTouchListener to all Views that can generate a ContextMenu:
private View.OnTouchListener onTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent)
{
mLastViewTouched = view; // Store a handle on the last view touched. This will be used to identify the view on which the Context Menu was launched
return false; // We return false since this indicates that the touch was not handled and so it is passed down the stack to be handled appropriately
}
};
So whenever a View is touched mLastViewTouched is updated. Now in onContextItemSelected you will have access to the View that initiated the ContextMenu.
When constructing your menu in OnCreateContextMenuListener or public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) implementation, you can set a custom MenuItem.OnMenuItemClickListener for each item:
addPhotosBtn.setOnCreateContextMenuListener((menu, v, menuInfo) -> {
getMenuInflater().inflate(R.menu.upload_image_menu, menu);
int itemCount = menu.size();
for(int i = 0; i < itemCount; i++) {
menu.getItem(i).setOnMenuItemClickListener(addPhotosBtnMenuItemClickListener);
}
});
Since at this time you have access to the view for which you are creating the context menu, you can tightly couple the listener with that view.
Here is what I did to distinguish between 2 listviews lstA and lstB MenuItem
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
int startid = (v.getId() == R.id.lstA) ? 0 : 2; //0-1 will be lstA and 2-3 will be lstB
menu.setHeaderTitle("some title");
menu.add(Menu.NONE, startid, Menu.NONE, "Item 1");
menu.add(Menu.NONE, startid+1, Menu.NONE, "Item 2");
}
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
int menuItemIndex = info.position;
switch(item.getItemId()) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
}
return true;
}
Related
Normally for ListViews, I would do this when I wanted to get the position that the user clicked on a Context Menu.
public boolean onContextItemSelected(android.view.MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
position = info.position;
However, ever since I switched to a RecycleView, I now get a null pointer here.
The code above is in my main Activity (Fragment) while onCreateContextMenu() is done in the adapter as per the new way.
ItemView.setOnCreateContextMenuListener(this); is also done in the adapter (specifically the constructor).
There're 3 options:
You can pass getAdapterPosition() instead of MenuItem's order
private class ChipViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener {
public ChipViewHolder(View itemView) {
super(itemView);
itemView.setOnCreateContextMenuListener(this);
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
menu.setHeaderTitle("Select The Action");
menu.add(0, ACTION_1_ID, getAdapterPosition(), "action 1");
menu.add(0, ACTION_2_ID, getAdapterPosition(), "action 2");
}
}
And then, in Activity listen to onContextItemSelected() and retrieve position by getOrder()
#Override
public boolean onContextItemSelected(MenuItem item) {
int clickedItemPosition = item.getOrder();
// do something!
return super.onContextItemSelected(item);
}
Use custom implementations of RecyclerView like Teovald/ContextMenuRecyclerView one
Setting MenuItem's clickListener (see https://stackoverflow.com/a/33179957/1658267 ) and handles it there.
Yes, it's very inconvenient API. You can choose the one you like most.
I have a simple layout where there are few textviews. Here what I do is define a name, and save it into a database using a contentProvider. Then, this name is read from the database and shown on the textView.
This is how I first get the item saved on the database:
Cursor c = getContentResolver().query(TravelersProvider.CONTENT_URI, PROJECTION, null, null, null);
The projection is:
String[] PROJECTION = {Travelers._ID, Travelers.NAME};
Now What I need to do is:
Create contextMenu for when I click on the textview with the name, I can edit or delete it.
This is the way I register the contextMenu to the TextView:
txtView1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
registerForContextMenu(v);
}
});
Then I create it:
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.context_menu, menu);
}
And finally I define what to do when touching:
#Override
public boolean onContextItemSelected(MenuItem item) {
//TODO:
}
Here is where I need some help. Basically, what I need to do is:
Define wich element is the one that I've touched. This should obtain an index.
Get the cursor and move it to that position.
In the onCreateContextMenu hook, there is a View parameter. Call getId() on this parameter to determine which TextView invoked the context menu. Then you can store this into a class variable to be used in onContextItemSelected.
private int contextViewId;
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.context_menu, menu);
contextViewId = v.getId();
}
#Override
public boolean onContextItemSelected(MenuItem item) {
// use contextViewId
}
I have a LisView with several items. To this I've connected an OnItemClickListener (as an inner class), like this:
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(ShoppingListApp02Activity.this, "List item selected:" +
items.get(position).getId(), Toast.LENGTH_LONG).show();
}
});
As is obvious, selecting an entriy displays elements of the object of that entry, in this example the selected Item object's ID (not the list ID, but the objects ID, set when creating the ArrayList items). This works nicely, and enables me to do anything I want with the selected item(s).
Now I'd like to also have a "long-click" listener her, which opens a context menu for the selected ListView item. How do I do that? I've been able to attach an onCreateContextMenu listener to the ListView, but I don't see how I can get the elements of the ArrayList as with the onItemClickListener?
Here's what I've got:
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
menu.add(0, v.getId(), 0, "Something");
menu.add(0, v.getId(), 0, "Something else");
}
Since OnCreateConextMenu takes different parameters than the OnItemClickListener, how to I access the ArrayList's elements like in the OnItemClickListener?
If you decide you still want to use the context menu paradigm:
Consider this for working with lists:
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
// Get the list
ListView list = (ListView)v;
// Get the list item position
AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
int position = info.position
// Now you can do whatever.. (Example, load different menus for different items)
list.getItem(position);
...
}
Instead of messing with context menus (which are used in a wide context - like right-click in PC), ListView offers onItemLongClick event which is a lot more easier to implement. For example:
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
return false;
}
});
This will help you to achieve long-pressed actions on a row.
Open the context menu of the view within the event handler for the long press event on the row view.
convertView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
((Activity)mContext).openContextMenu(v);
return true;
}
});
This way both the click on the view and the long press context menu works on the listview row item.
First register context menu into your listview to open context menu:
registerForContextMenu(YOUR LIST-VIEW OBJECT);
then you can use your onCreateContextMenu() method:
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
menu.add(0, v.getId(), 0, "Something");
menu.add(0, v.getId(), 0, "Something else");
}
You actually dont need to use longClickListener with your listview to use ContextMenu.
Hope it will help you.
I will let you go through the below written example and see how it is implemented by using onContextItemSelected()
public void onCreateContextMenu(ContextMenu menu,
View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.setHeaderTitle(title);
menu.add(0, CMD_EDIT, 0, R.string.context_menu_edit);
menu.add(0, CMD_DELETE, 0, R.string.context_menu_delete);
}
#Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case CMD_EDIT:
any_function();//add your functionality here i.e. what you want to do
return true;
case CMD_DELETE:
**confirmDelete**();
return true;
default:
return super.onContextItemSelected(item);
}
}
Hope this helps...
Try this for a View item in recycleView
viewForContextMenu.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
menu.add("Context Menu Item1").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
//What should happent on selecting context menu item 1
return true;
}
});
}
});
You can use it with setting data to a ViewHolder item
I have a CustomListAdapter. I have overloaded my OnItemClickListener and added a registerForContextMenu line for the position that i have a context menu shown.
When i select the Item that should show the MenuItem, the menu is shown. When i press the back button on the phone the menu disappears. However now what happens that the same Item in my listview does not receive the OnItemClickListener anymore. Am i making sense ? I mean after the menu disappears, the same item does not receive the click listener. The items above and below receive the event as desired. I seems as if the Menu has disappeared but still is catching the click event ?
It's bad. You have to call registerForContextMenu in onCreate method.
So try it like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contacts);
registerForContextMenu(<yourListView>);
...
}
for create ContextMenu you have to override onCreateContextMenu method
#Override
public void onCreateContextMenu(ContextMenu cMenu, View parent, ContextMenu.ContextMenuInfo info) {
this.contextMenu = cMenu;
new MenuInflater(Contacts.this).inflate(R.menu.conmenu, this.contextMenu);
}
and for select items override onContextItemSelected method:
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case R.id.c_odobrat:
deleteContactDialog(info.id);
return true;
}
return false;
}
And it should works.
quickLinkListView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id)
{
Intent intent = new Intent();
intent.setClassName(Home.this, "com.myapp.toc" + Constants.ACT_NAMES[position]);
if (position < 4 && position > 1)
{
switch (position)
{
case 3:
registerForContextMenu(v);
ViewHolder.v=v;
openContextMenu(v);
break;
}
}
}
});
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
{
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.context_home, menu);
menu.setHeaderTitle("Select Video Type");
}
I have implemented a custom list view. (using Activity and ListRowAdapter extending BaseRowAdaptor)
I set a contextmenu to listview setting
itemView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener()
{
#Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo)
{
menu.setHeaderTitle("Context Menu");
menu.add(0, v.getId(), 0, "Action 1");
// menu.add(0, v.getId(), 0, "Action 2");
}
});
In getView method in my adapter class.
But How t handle the item select event?
I can catch that event in My Activity.
But how can i identify the row of the list view?
You can create a global int variable that contains the index of the item that you have selected. You would set the value of the variable in the listview onLongClick(), onClick(), etc event. Then when you are inside the context menu's item click event, you will have access the currently selected row in the listview.
set LongClickListener to listview then showContext menu on longClick .
Class ActivityName extends Activity implements IconContextItemSelectedListener
{
private IconContextMenu cmContextMenu;
private int selectedPosition ;
onCreate() {
cmContextMenu = new IconContextMenu(this, R.menu.fbupdatecontextmenu);
cmContextMenu.setOnIconContextItemSelectedListener(this);
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> av, View v, int position, long arg3) {
selectedPosition = position;
cmContextMenu.show();
return true;
}
});
}
public void onIconContextItemSelected(MenuItem item, Object info) {
switch (item.getItemId()) {
case R.id.someThing: {
system.out.prinlt("position " +selectedPosition);
break;
}
}
}