I have a simple AlertDialog
that displays a list of some items and upon clicking one of them, the clicked item is passed back to the enclosing Activity
. I also want to perform some default handling when the user cancels the dialog (using the back button) - more specifically, I want to pass an empty string to the activity in such case.
However, if I put the dialog in a DialogFragment
(from the compatibility package), the OnCancelListener
is not called when I close the dialog with the back button. What am I doing wrong?
public class SelectItemDialog extends DialogFragment {
public interface Callback {
void onItemSelected(String string);
}
private static final String ARG_ITEMS = "items";
private Callback callback;
public static SelectItemDialog fromItems(Collection<String> items) {
SelectItemDialog fragment = new SelectItemDialog();
fragment.setArguments(newArguments(items));
return fragment;
}
private static Bundle newArguments(Collection<String> items) {
Bundle arguments = new Bundle();
arguments.putStringArray(ARG_ITEMS, items.toArray(new String[items.size()]));
return arguments;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback = (Callback) activity;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final String[] items = getArguments().getStringArray(ARG_ITEMS);
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.dialog_select_email_title)
.setItems(items, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onItemSelected(items[which]);
}
})
.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// this code is not executed
callback.onItemSelected("");
throw new RuntimeException("dialog cancelled");
}
})
.create();
}
}
It might have to do with the fact that there is no explicit call to cancel()
from your code.
The OnCancelListener documentation says:
This will only be called when the dialog is canceled
Which probably needs an explicit cancel()
call.
Either make a positive/negative button with a OnClickListener
that calls DialogInterface#cancel()
or use a OnDismissListener()
with an extra check to see if a list item was clicked.
Also, to listen for a back keypress and cancel the dialog, you can set up an OnKeyListener
, like outlined in this SO answer
Also, once you have the Dialog set up, it would also be a good idea to use Dialog#setCanceledOnTouchOutside()
in case the the user taps outside the Dialog.
Edit: The below part is the easy way to handle cancel events in a DialogFragment.
Since you are using a DialogFragment
, this class has a very handy method, DialogFragment#onCancel()
which gets called when the DialogFragment
is cancelled. Do your logic in there.
DialogFragments are more complex, with a slightly different lifecycle than normal dialogs. Therefore, first check the documentation if you have a certain Dialog
-based approach that you are trying to port to a DialogFragment
, some methods may exist that allow your new implementation to function properly!
If you are using DialogFragment and want to listen back button then use this -
this.getDialog().setOnKeyListener(new Dialog.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode,
KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (****) {
your logic
}
return true;
}
return false;
}
});
Note: DialogFragment own the Dialog.setOnCancelListener and Dialog.setOnDismissListener callbacks. You must not set them yourself.
To find out about these events, override onCancel(DialogInterface) and onDismiss(DialogInterface).
public class SelectItemDialog extends DialogFragment {
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
//your code hear
dialog.cancel();
}
}
And you should remove .setOnCancelListener()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With