Как стать автором
Обновить

Комментарии 15

А бывают ли реальные потребности отправлять события прямо во Activity? Может тогда и вызывать DialogFragment в самом Activity и имплементировать ему DialogFragment абстрактные методы?
А бывают ли реальные потребности отправлять события прямо во Activity?
К примеру, если UI реализован в Activity без использования Fragment.
Может тогда и вызывать DialogFragment в самом Activity и имплементировать ему DialogFragment абстрактные методы?
На сколько я понимаю, речь об анонимном классе. Я бы такой подход не использовал т.к я вижу всего один сомнительный плюс и два существенных минуса.
Сомнительный плюс: чуть меньше кода (не будет создания констант REQUEST_, не будет switch и не надо использовать intent).
Минусы:
  • Анонимный класс хранит ссылку на внешний класс. Как результат, усложняется задача по обработке пересоздания фрагментов т.к. помимо сохранения/восстановления собственных данных нам нужно будет еще и эту ссылку воостанавливать. Решаемо конечно, но уже усложнение и дополнительная возможность ошибиться.
  • Весь код будет в вызывающих activity/fragment, а значит повторно использовать DialogFragment уже нельзя.
На сколько я помню, при пересоздании анонимного диалог фрагмента выбрасывается исключение ClassNotFoundException. Так что это не просто сомнительно, а крайне не желательно.
На сколько я понимаю, речь об анонимном классе.

Нет, речь о абстрактном классе (не inner) DialogFragment, инстанс которого создается прямо в Activity по вызову такого же абстрактного метода во Fragment. Все эти абстрактные методы имплементируются на уровне инстанса, который нам надо слушать. По аналогии интерфейса.
Вы говорите, что это не анонимный класс, но то, что вы описываете и есть анонимный класс.
Если я правильно понял, вы имели ввиду вот такую реализацию:
DialogFragment dialog = new AbsDialogFragment() {
    @Override
    public void someAbstractMethod(String someString) {
        // Implementation ...
    }
};
dialog.show(getFragmentManager(), "ImplDialogFragment");

Это яркий пример анонимного класса.
Еще, для получения фрагмента, вызвавшего диалог, можно использовать метод getParentFragment(). Но для этого, при вызове диалога, нужно использовать ChildFragmentManager. Минус этого метода заключается в том, что для диалога нельзя будет установить setRetainInstance(true). Но как по мне так setRetainInstance(true) — это читерский прием, который может привести к кое-каким неприятностям в будущем, при уничтожении процесса.
Думаю этот подход лучше. Использую его везде.
Во все проекты добавляю что-то похожее на такой BaseDialogFragment
Считаю, что getParentFragment() хорош если у нас один вызывающий фрагмент и один вызываемый фрагмент (отношение один к одному). Если же у нас несколько вызываемых фрагментов (отношение один ко многим), то необходимо иметь возможность различить их с помощью getTargetRequestCode(), который устанавливается через setTargetFragment.
RequestCode можно и через аргументы передать. А еще в ответе можно возвращать Tag фрагмента. Так что путей идентификации масса.
Популярное решение для связей один-к-одному между фрагментами. К сожалению, не масштабируется на другие случаи вне фрагментов или когда нужно оповестить несколько компонентов системы. Как результат, чтобы не городить отдельную систему коллбэков для каждого отдельного случая, рано или поздно приходят к более универсальному event bus паттерну. Как бонус — объекты вместо intent.
А я комбинирую вариант имплементации кастомного интерфейса и вариант с использованием методов setTargetFragment и getTargetFragment. Выглядит это так: хост-фрагмент (вызывающий диалог) имплементирует интерфейс-колбек, далее в диалоге он устанавливается как target-фрагмент, при возврате результата getTargetFragment кастится к интерфейсу и дергается колбек. Плюсы такого подхода перед описываемым — отсутствие необходимости упаковывать-распаковывать результаты в intent, более очевидный и чистый код.
Полный код такого фрагмента тут
Он позволяет создавать как простые фрагменты-диалоги типа
new ConfirmDialogFragment.Builder(this)
    .title(R.string.menu_logout_confirm_dialog_title)
    .message(R.string.menu_logout_confirm_dialog_text)
    .show();

… так и более сложные с передачей диалог-тега (для идентификации конкретного диалога в колбеке), установкой стиля, передачи дополнительных данных (cookie), которые будут доступны в колбеке.
new ConfirmDialogFragment.Builder(this)
    .title(R.string.menu_logout_confirm_dialog_title)
    .message(R.string.menu_logout_confirm_dialog_text)
    .okButton(R.string.button_logout)
    .dialogTag("confirmLogout")
    .cookie("user", user)
    .destructive()
    .show();


Все это нормально переживает пересоздание активити.
Хорошее решение, но работает только, если оба фрагмента находятся в бек-стеке.
Если это не будет удовлетворено, то фрагмент менеджер упадет при изменении конфигурации (target not in fragment manager).
Понимаю, что вопрос немного не по адресу, но все же. При использовании DialogFragment (из саппорт либы) у меня на девайсах старше киткат 4.4.4 вместо тени — черная рамка вокруг диалога, а без DialogFragment — все работает. Может быть встречалось такое?
Спасибо, тебе добрый человек, за статью.
Вчера пол-ночи возился с такой задачей.
Причем, раньше так делал — но напрочь забыл.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.