Detecting which selected item (in a ListView) spawned the ContextMenu (Android)

AndroidListviewContextmenu

Android Problem Overview


I have a ListView that will allow the user to long-press an item to get a context menu. The problem I'm having is in determining which ListItem they long-pressed. I've tried doing this:

myListView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
  @Override public void onCreateContextMenu(ContextMenu menu, final View v, ContextMenuInfo menuInfo) {
   menu.add("Make Toast")
    .setOnMenuItemClickListener(new OnMenuItemClickListener() {
     @Override public boolean onMenuItemClick(MenuItem item) {
      String toastText = "You clicked position " + ((ListView)v).getSelectedItemPosition();
      Toast.makeText(DisplayScheduleActivity.this, toastText, Toast.LENGTH_SHORT).show();
      return true;
     }
    });
  } 
 });

but it just hangs until an ANR pops up. I suspect that after the menu is created the ListItem is no longer selected.

It looks like you could monitor for clicks or long-clicks then record the clicked item there:

 mArrivalsList.setOnItemLongClickListener(new OnItemLongClickListener() {
  @Override public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) {
   // record position/id/whatever here
   return false;
  }
 });

but that feels majorly kludgey to me. Does anyone have any better solutions for this?

Android Solutions


Solution 1 - Android

I do exactly this. In my onCreateContextMenu(...) method, I cast the ContextMenu.ContextMenuInfo to AdapterView.AdapterContextMenuInfo. From there, you can get the targetView, which you cast again to the widget. The complete code is available in HomeActivity.java, look for the onCreateContextMenu(...) method.

@Override
public void onCreateContextMenu(ContextMenu contextMenu,
                                View v,
                                ContextMenu.ContextMenuInfo menuInfo) {
    AdapterView.AdapterContextMenuInfo info =
            (AdapterView.AdapterContextMenuInfo) menuInfo;
    selectedWord = ((TextView) info.targetView).getText().toString();
    selectedWordId = info.id;

    contextMenu.setHeaderTitle(selectedWord);
    contextMenu.add(0, CONTEXT_MENU_EDIT_ITEM, 0, R.string.edit);
    contextMenu.add(0, CONTEXT_MENU_DELETE_ITEM, 1, R.string.delete);
}

Note that I store the selected text as well as the select id in private fields. Since the UI is thread confined, I know the selectedWord and selectedWordId fields will be correct for later actions.

Solution 2 - Android

First of all, I'm wondering if you're making things a little overly complicated by using View.setOnCreateContextMenuListener(). Things get a lot easier if you use Activity.registerForContextMenu(), because then you can just use Activity.onCreateContextMenu() and Activity.onContextItemSelected() to handle all of your menu events. It basically means you don't have to define all these anonymous inner classes to handle every event; you just need to override a few Activity methods to handle these context menu events.

Second, there's definitely easier ways to retrieve the currently selected item. All you need to do is keep a reference either to the ListView or to the Adapter used to populate it. You can use the ContextMenuInfo as an AdapterContextMenuInfo to get the position of the item; and then you can either use ListView.getItemAtPosition() or Adapter.getItem() to retrieve the Object specifically linked to what was clicked. For example, supposing I'm using Activity.onCreateContextMenu(), I could do this:

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);

    // Get the info on which item was selected
    AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;

    // Get the Adapter behind your ListView (this assumes you're using
    // a ListActivity; if you're not, you'll have to store the Adapter yourself
    // in some way that can be accessed here.)
    Adapter adapter = getListAdapter();

    // Retrieve the item that was clicked on
    Object item = adapter.getItem(info.position);
}

@Override
public boolean onContextItemSelected(MenuItem item) {
	// Here's how you can get the correct item in onContextItemSelected()
	AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
	Object item = getListAdapter().getItem(info.position);
}

Solution 3 - Android

this is another way on how to create context menu n how to delete the item selected here is the whole code

     public class SimpleJokeList extends Activity {
public static final int Upload = Menu.FIRST + 1;
public static final int Delete = Menu.FIRST + 2;
int position;
ListView lv;
EditText jokeBox;
Button addJoke;
MyAdapter adapter;
private ArrayAdapter<String> mAdapter;
private ArrayList<String> mStrings = new ArrayList<String>();
String jokesToBeAdded;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.simplejokeui);
    
   
    
    lv=(ListView)findViewById(R.id.jokelist);
    addJoke=(Button)findViewById(R.id.addjoke);
    jokeBox=(EditText)findViewById(R.id.jokebox);
    
   
    mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mStrings);
    
 
    registerForContextMenu(lv);
    listItemClicked();
    addJokes();

private void addJokes() {
	// TODO Auto-generated method stub
	addJoke.setOnClickListener(new OnClickListener(){

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			jokesToBeAdded=jokeBox.getText().toString();
			if(jokesToBeAdded.equals("")){
			Toast.makeText(getApplicationContext(), "please enter some joke", Toast.LENGTH_LONG).show();
			}
			else{
				lv.setAdapter(mAdapter);
				mAdapter.add(jokesToBeAdded);
				jokeBox.setText(null);
			}	
		}
    });
}
private void listItemClicked() {
	// TODO Auto-generated method stub
	lv.setOnItemLongClickListener(new OnItemLongClickListener() {

		@Override
		public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
				int arg2, long arg3) {
			// TODO Auto-generated method stub
			position=arg2;
			return false;
		}
	});
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
		ContextMenuInfo menuInfo) {
	// TODO Auto-generated method stub
	super.onCreateContextMenu(menu, v, menuInfo);
	populateMenu(menu);
	menu.setHeaderTitle("Select what you wanna do");

}
private void populateMenu(ContextMenu menu) {
	// TODO Auto-generated method stub
	 menu.add(Menu.NONE, Upload, Menu.NONE, "UPLOAD");
        menu.add(Menu.NONE, Delete, Menu.NONE, "DELETE");
}
 @Override
    public boolean onContextItemSelected(MenuItem item) 
    {
     return (applyMenuChoice(item) || super.onContextItemSelected(item));
    }


private boolean applyMenuChoice(MenuItem item) {
	// TODO Auto-generated method stub
	switch (item.getItemId()) 
	{	
         case Delete:
        	 
        	 String s=mAdapter.getItem(position);
        	 mAdapter.remove(s);
        	// position--;
        	 Toast.makeText(getApplicationContext(),"Congrats u HAve Deleted IT", Toast.LENGTH_LONG).show();
        return (true);
	}
	return false;
}

Solution 4 - Android

And don't forget to put this

registerForContextMenu(listview);

in your onCreate method to get your context Menu visible.

Solution 5 - Android

Isn't the view argument the actual selected row's view, or am I missing the question here?

ListView lv;
private OnItemLongClickListener onLongClick = new OnItemLongClickListener() {
	public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
			int arg2, long arg3) {
		lv.showContextMenuForChild(arg1);
		lv.showContextMenu();
		return false;
	}
};

Solution 6 - Android

We have used with success:

@Override
public boolean onContextItemSelected
(
MenuItem item
)
    {
    if (!AdapterView.AdapterContextMenuInfo.class.isInstance (item.getMenuInfo ()))
        return false;
    
    AdapterView.AdapterContextMenuInfo cmi =
        (AdapterView.AdapterContextMenuInfo) item.getMenuInfo ();
    
    Object o = getListView ().getItemAtPosition (cmi.position);

    return true;
    }

Solution 7 - Android

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
	super.onCreateContextMenu(menu, v, menuInfo);
	AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
	View TargetV=(View) info.targetView;
	text1 = (String) ((TextView) TargetV.findViewById(R.id.textView1)).getText();
	text2 = (String) ((TextView) TargetV.findViewById(R.id.textView2)).getText();
	if(List3Ok){
		text3 = (String) ((TextView) TargetV.findViewById(R.id.textView3)).getText();	
	}
	selectedWord = text1 + "\n" + text2 + "\n" + text3;
    selectedWordId = info.id;
    menu.setHeaderTitle(selectedWord);
	MenuInflater inflater = this.getActivity().getMenuInflater();
    inflater.inflate(R.menu.list_menu, menu);
}

Solution 8 - Android

I case you are using SimpleCursorAdapder you may do it like this

	@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
	getActivity().getMenuInflater().inflate(R.menu.project_list_item_context, menu);
	
	// Getting long-pressed item position
	AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
    int position = info.position;
	
	// Get all records from adapter 
	Cursor c = ((SimpleCursorAdapter)getListAdapter()).getCursor();						
	
	// Go to required position
	c.moveToPosition(position);
	
	// Read database fields values associated with our long-pressed item
	Log.d(TAG, "Long-pressed-item with position: " + c.getPosition());
	Log.d(TAG, "Long-pressed-item _id: " + c.getString(0));
	Log.d(TAG, "Long-pressed-item Name: " + c.getString(1));
	Log.d(TAG, "Long-pressed-item Date: " + c.getString(2));
	Log.d(TAG, "Long-pressed-item Path: " + c.getString(3));
	
	// Do whatever you need here with received values
	
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJeremy LoganView Question on Stackoverflow
Solution 1 - AndroidEric BurkeView Answer on Stackoverflow
Solution 2 - AndroidDan LewView Answer on Stackoverflow
Solution 3 - AndroidAli AshrafView Answer on Stackoverflow
Solution 4 - Androidsami boussacsouView Answer on Stackoverflow
Solution 5 - AndroidPatrick KafkaView Answer on Stackoverflow
Solution 6 - AndroidNateView Answer on Stackoverflow
Solution 7 - AndroidOmid OmidiView Answer on Stackoverflow
Solution 8 - AndroidAndrey BulanovView Answer on Stackoverflow