context menus and transmitting state to a dialog

I basically have the same problem as reported here: putting a button or other focus-receiving object on a ListView blocks it from receiving item selections because those need to be focus-receiving too. Tricky. I guess that makes sense, though, how would you navigate without a touch-screen? So I guess it’ll be context menus for my “log it now” function although there could be ways around it on a touch screen.

Next problem: I’m using a context menu and one of the functions is to delete. Before deleting something the user accidentally touched, I want to show a dialog, then delete it if they confirm. Problem: there doesn’t seem to be any way to hold on to the item in question while the dialog is shown, except for using a class member (which seems really messy).

public boolean onContextItemSelected(MenuItem item) {
    Log.d(TAG, "onContextItemSelected");
    switch(item.getItemId()) {
    case R.id.ilc_menu_delete:
        showDialog(DELETE_DIALOG);
        return true;
    }
    return super.onContextItemSelected(item);
}

You can’t pass any information to showDialog; the dialog is actually shown via a separate callback. In fact, it’s worse than that – I don’t even have information when the context menu is clicked, or when it’s created from the list item!

OK – checked the docs; you can get the item that was selected, it’s just rather convoluted:

  AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
  // now info.id is the rowid given by the adapter for the item

But I’d still have to stash that ID somewhere while the dialog is shown. So for now I’m putting it in a member for the transition. Once the dialog is created, the ID can be captured in a listener. That works OK. The indirection for requesting a dialog versus showing it is a bit puzzling.


I gather that the reason for the indirection is so the Activity can “manage” the dialog. And looking a little more closely at the docs here, capturing the ID in a listener at dialog creation time won’t work either, because the dialog is re-used; when I want to delete a different item, the dialog still has a listener that deletes the first item. OK, I could use onPrepareDialog to set a new listener each time it’s displayed; but come to think of it, should the listener be responsible for performing the actual delete? The dialog is just supplying a yes/no. I’d like my activity to just display the dialog directly and use the response to determine whether to do the delete. But now I’m off the recommended path, so treading lightly.
Minor point: dialogs have a “setOnDismissListener” setter, but the AlertDialog.Builder (at least in 1.6) doesn’t have this setter – if you want to use it, have to set it after creating the dialog (which kind of defeats the purpose of using the builder).

In the end, I went with a straightforward dialog creation call from the context menu event:

private void showDeleteDialog(final long itemId) {
	Log.d(TAG, "showDeleteDialog");
	Dialog d = new AlertDialog.Builder(this)
	.setTitle(R.string.ilc_dialog_delete_title)
	.setMessage(R.string.ilc_dialog_delete_msg)
	.setNegativeButton(R.string.ilc_dialog_cancel_button, null)
	.setPositiveButton(R.string.ilc_dialog_delete_button, new OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				Log.d(TAG, "DeleteDialog onClick " + itemId);
				mDba.deleteItem(itemId);
				fillItemList();
			}
		})
	.create();
	d.setOwnerActivity(this); // why can't the builder do this?
	d.show();

}

I was trying to go the standard dialog management route and creating an inner class to handle the state, and that became unwieldy; this is much better. It does create an anonymous inner class, but in this case it’s really the right thing to do.

private void showDeleteDialog(final long itemId) {
Log.d(TAG, “showDeleteDialog”);
Dialog d = new AlertDialog.Builder(this)
.setTitle(R.string.ilc_dialog_delete_title)
.setMessage(R.string.ilc_dialog_delete_msg)
.setNegativeButton(R.string.ilc_dialog_cancel_button, null)
.setPositiveButton(R.string.ilc_dialog_delete_button, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(TAG, “DeleteDialog onClick ” + itemId);
mDba.deleteItem(itemId);
fillItemList();
}
})
.create();
d.setOwnerActivity(this); // why can’t the builder do this?
d.show();}

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: