OnCancelListener не вызывается в DialogFragment

У меня есть простой AlertDialog, который отображает список некоторых элементов, и при нажатии на один из них щелкнутый элемент передается обратно в охватывающий Activity. Я также хочу выполнить некоторую обработку по умолчанию, когда пользователь отменяет диалоговое окно (используя кнопку «Назад») — точнее, я хочу передать пустую строку в действие в таком случае.

Однако, если я поместил диалоговое окно в DialogFragment (из пакета совместимости), OnCancelListener не вызывается, когда я закрываю диалоговое окно кнопкой "Назад". Что я делаю не так?

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();
    }
}

person Natix    schedule 09.02.2013    source источник


Ответы (5)


Возможно, это связано с тем, что в вашем коде нет явного вызова cancel(). В документации OnCancelListener говорится:

Это будет вызываться только при отмене диалога

Что, вероятно, требует явного вызова cancel().

Либо создайте кнопку "положительно/отрицательно" с помощью OnClickListener, которая вызывает DialogInterface#cancel() или используйте OnDismissListener() с дополнительной проверкой посмотреть, был ли нажат элемент списка.

Кроме того, для прослушивания обратного нажатия клавиши и отмены диалога вы можете настроить OnKeyListener, как описано в этот ответ SO< /а>

Кроме того, после настройки диалогового окна было бы неплохо использовать Dialog#setCanceledOnTouchOutside() в случае, если пользователь касается вне диалогового окна.

Редактировать: приведенная ниже часть представляет собой простой способ обработки событий отмены в DialogFragment.

Поскольку вы используете DialogFragment, у этого класса есть очень удобный метод, DialogFragment#onCancel(), который вызывается при отмене DialogFragment. Включите туда свою логику.

DialogFragments более сложны, их жизненный цикл немного отличается от обычных диалогов. Поэтому сначала проверьте документацию, если у вас есть определенный подход на основе Dialog, который вы пытаетесь перенести на DialogFragment, могут существовать некоторые методы, позволяющие вашей новой реализации работать правильно!

person A--C    schedule 09.02.2013
comment
Я отредактировал свой вопрос. Я хочу, чтобы код в OnCancelListener вызывался, когда диалог закрывается с помощью кнопки «Назад». - person Natix; 09.02.2013
comment
@Natix см. этот ответ, он делает то же самое. Вы должны добавить OnKeyListener, чтобы следить за нажатием клавиши «Назад». - person A--C; 09.02.2013
comment
Что ж, это решение, вероятно, должно работать, но если я создаю диалог непосредственно внутри действия без DialogFragment, то вызывается OnCancelListener - как при нажатии кнопки «Назад», так и при нажатии пользователем вне диалогового окна. Я не понимаю, почему DialogFragment меняет поведение. - person Natix; 09.02.2013
comment
DialogFragment#onCancel() это именно то, что я искал! Кажется, что методы DialogFragment переопределяют слушателей содержащегося AlertDialog. Подобным случаем является и метод setCancelable(). Если вы интегрируете это в свой ответ, я приму это. - person Natix; 10.02.2013
comment
@Natix обновил мой ответ, хорошо, что я проверил документацию! - person A--C; 10.02.2013

Если вы используете DialogFragment и хотите прослушать кнопку, используйте это -

    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;
        }
    });
person Nikhil Pingle    schedule 09.09.2013
comment
Спасибо, что действительно ответили на вопрос! это была часть getDialog, которая сбила меня с толку! - person Aiden Fry; 26.02.2014

Примечание. DialogFragment владеет обратными вызовами Dialog.setOnCancelListener и Dialog.setOnDismissListener. Вы не должны устанавливать их самостоятельно.

Чтобы узнать об этих событиях, переопределите onCancel(DialogInterface) и onDismiss(DialogInterface).

public class SelectItemDialog extends DialogFragment {

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        //your code hear
        dialog.cancel();
    }
}

И вы должны удалить .setOnCancelListener()

person Bao Le    schedule 29.12.2015
comment
Хорошо, это должен быть правильный ответ. Этот маленький трюк не очень понятен в этом интерфейсе. - person Brill Pappin; 21.03.2018

На самом деле, если вы хотите использовать DialogFragment, вы никогда не сможете добавить к нему OnCancelListener или OnDismissListener, поскольку фрагмент диалога владеет обратными вызовами для этих методов!

Здесь у вас есть 3 варианта:

1- используйте обычные диалоги.
2- установите для фрагмента диалога значение "отменяемый" (false) и добавьте в диалог кнопку отмены.
3- проверьте ответ @Nikhil Pingle.

это из документации фрагмента диалога

 * <p><em>Note: DialogFragment own the {@link Dialog#setOnCancelListener
 * Dialog.setOnCancelListener} and {@link Dialog#setOnDismissListener
 * Dialog.setOnDismissListener} callbacks.  You must not set them yourself.</em>
 * To find out about these events, override {@link #onCancel(DialogInterface)}
 * and {@link #onDismiss(DialogInterface)}.</p>
person Muhammad Alfaifi    schedule 25.09.2014

Отменить прослушиватель или отклонить прослушиватель в DialogFragment можно с помощью onDismiss

            DialogFragment  newFragment = new DatePickerFragment();
            newFragment.show(getFragmentManager(), "datePicker");
            newFragment.onDismiss(new DialogInterface(){

                @Override
                public void cancel() {
                    // TODO Auto-generated method stub

                }

                @Override
                public void dismiss() {
                    // TODO Auto-generated method stub

                }

            });
person Munish Kapoor    schedule 02.10.2014
comment
Этот код фактически заставит диалоговое окно отображаться и немедленно закрываться из-за вызова DialogFragment.onDismiss(...). Пользователь его даже не увидит. - person Maciej Pigulski; 05.11.2014