Как правильно закрыть DialogFragment?

121

Документы говорят об этом для dismiss()метода из Dialogкласса:

Закройте это диалоговое окно, удалив его с экрана. Этот метод можно безопасно вызывать из любого потока. Обратите внимание, что вы не должны переопределять этот метод для очистки при закрытии диалогового окна, вместо этого реализуйте это в onStop().

В моем коде все, что я делаю, - это призываю getDialog().dismiss()его закрыть. Но больше ничего не делаю и даже не использую onStop(). Поэтому я спрашиваю, как правильно закрыть, DialogFragmentчтобы избежать утечек памяти и т. Д.

Энди
источник

Ответы:

197

tl; dr: Правильный способ закрыть a DialogFragment- использовать dismiss() непосредственно в DialogFragment .


Подробности : в документации DialogFragment говорится

Управление диалоговым окном (принятие решения о том, когда его показать, скрыть или закрыть) должно осуществляться через API, а не с помощью прямых вызовов диалогового окна.

Таким образом, вы не должны использовать getDialog().dismiss(), так как это вызовет dismiss() диалоговое окно . Вместо этого вы должны использовать dismiss()метод самого DialogFragment:

общественная недействительность увольнение ()

Закройте фрагмент и его диалог. Если фрагмент был добавлен в задний стек, все состояния заднего стека до этой записи включительно будут извлечены. В противном случае будет зафиксирована новая транзакция для удаления фрагмента.

Как видите, это касается не только закрытия диалогового окна, но и обработки транзакций фрагментов, участвующих в процессе.

Вам нужно использовать только в том onStopслучае, если вы явно создали какие-либо ресурсы, требующие ручной очистки (закрытие файлов, закрытие курсоров и т. Д.). Даже тогда я бы переопределил onStopDialogFragment, а не onStopлежащий в основе Dialog.

Heinzi
источник
1
@ScootrNova: Не должно быть, у вас, вероятно, есть ошибка где-то еще. Как вы создаете фрагмент?
Heinzi
protected void showDialogFragment(final DialogFragment fragment) {final FragmentTransaction fTransaction = getSupportFragmentManager().beginTransaction(); fTransaction.addToBackStack(null); fragment.show(fTransaction, "dialog");} Извините за неприятный лайнер! Но да, вы могли быть правы, поэтому пока я написал другой способ закрыть мои DialogFragments. Я отклонял их с помощью метода dismiss (), просто находил фрагмент по тегу и затем запускал на нем dismiss (), если он не был нулевым. Ах да, я newвставляю фрагмент прямо перед тем, как передать его этому методу.
Charles Madere
2
@ScootrNova: Хм, не вижу в этом ничего плохого - с другой стороны, я никогда не использовал библиотеку совместимости, поэтому я не могу быть уверен в этом. Возможно, имеет смысл создать минимальный, самодостаточный пример и задать по нему новый вопрос.
Heinzi
@CharlesMadere в те дни вы нашли решение?
JCarlosR
Извини, @JCarlos, это было много лет назад, я не уверен.
Чарльз Мадере
76

Я думаю, что лучший способ закрыть DialogFragmentэто:

Fragment prev = getSupportFragmentManager().findFragmentByTag("fragment_dialog");
if (prev != null) {
    DialogFragment df = (DialogFragment) prev;
    df.dismiss();
}

Таким образом, вам не нужно держать ссылку на объект DialogFragmentи закрывать его отовсюду.

Тер
источник
7

Почему бы вам не попробовать использовать только этот код:

dismiss();

Если вы хотите закрыть фрагмент диалогового окна самостоятельно. Вы можете просто поместить этот код внутрь фрагмента диалогового окна, из которого вы хотите закрыть диалоговое окно.

Например:

button.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       dismiss();
   }
});

Это закроет последний фрагмент диалогового окна, отображаемый на экране.

Надеюсь, это поможет вам.

Шива Тивари
источник
не работает все время
Махмуд Херетани
5

Я поддержал ответ Терел. Я просто хотел опубликовать это для всех пользователей Kotlin:

supportFragmentManager.findFragmentByTag(TAG_DIALOG)?.let {
    (it as DialogFragment).dismiss()
}
JustinMorris
источник
Простой код усердно работает, спасибо за обновления, приятель !!
Аюш Катувал
4

Котлинская версия ответа Терел

(fragmentManager.findFragmentByTag(TAG) as? DialogFragment)?.dismiss()
Фил
источник
1

Вы должны уволить вас Dialogв onPause()так переопределить его.

Также перед увольнением вы можете проверить nullи показать, как показано ниже:

@Override
protected void onPause() {
    super.onPause();
    if (dialog != null && dialog.isShowing()) {
        dialog.dismiss();
    }
}
Venky
источник
он уже написал, что делает dismiss () и его про DialogFragment.
Пареш Майани
Я думаю, что это работает как для Dialog, так и для DialogFragments @PareshMayani
Venky
2
Я считаю, что @PareshMayani прав Венки. Учебник DialogFragmentот Google вообще не показывает используемый onPause()метод. Но мне кажется, я понимаю, что вы делаете. В чем смысл, если пользователь не звонит onPause(). Тогда система знает, что фрагмент вызывается. А когда, скажем, пользователь отменяет подписку. Как лучше закрыть это в таком случае?
Энди
1

В других ответах есть ссылки на официальные документы ( DialogFragment Reference ), но нет упоминания о приведенном там примере:

void showDialog() {
    mStackLevel++;

    // DialogFragment.show() will take care of adding the fragment
    // in a transaction.  We also want to remove any currently showing
    // dialog, so make our own transaction and take care of that here.
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    Fragment prev = getFragmentManager().findFragmentByTag("dialog");
    if (prev != null) {
        ft.remove(prev);
    }
    ft.addToBackStack(null);

    // Create and show the dialog.
    DialogFragment newFragment = MyDialogFragment.newInstance(mStackLevel);
    newFragment.show(ft, "dialog");
}

Это удаляет все отображаемые в данный момент диалоги, создает новый DialogFragment с аргументом и показывает его как новое состояние в заднем стеке. Когда транзакция появляется, текущий DialogFragment и его Dialog будут уничтожены, а предыдущий (если он есть) будет повторно показан. Обратите внимание, что в этом случае DialogFragment позаботится о том, чтобы транзакция Dialog была отклонена отдельно от нее.

Для своих нужд я изменил его на:

FragmentManager manager = getSupportFragmentManager();
Fragment prev = manager.findFragmentByTag(TAG);
if (prev != null) {
    manager.beginTransaction().remove(prev).commit();
}

MyDialogFragment fragment = new MyDialogFragment();
fragment.show(manager, TAG);
Максим Иванов
источник
1

Добавляя к другим ответам, при наличии DialogFragmentполноэкранного вызова dismiss()DialogFragment не будет выталкиваться из стека фрагментов. Обходной путь - вызвать onBackPressed()родительскую операцию.

Что-то вроде этого:

CustomDialogFragment.kt

closeButton.onClick {
    requireActivity().onBackPressed()
}
mikehc
источник
Спаси день, большое спасибо
Махмуд Херетани
0

Просто вызовите dismiss () из фрагмента, который хотите закрыть.

imageView3.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            dismiss();
        }
    });
ВИВЕК ЧУДХАРЫ
источник
0

Я обнаружил, что когда мой фрагмент был определен в навигационном графе с помощью <fragment>тега (для полноэкранного диалогового фрагмента), диалоговый фрагмент не закрывался с помощью dismiss()команды. Вместо этого мне пришлось вытащить задний стек:

findNavController(getActivity(), R.id.nav_host_fragment).popBackStack();

Однако, если тот же самый фрагмент диалога был определен в графе навигации с помощью <dialog>тега, dismiss()работает нормально.

джон
источник
0
CustomFragment dialog = (CustomDataFragment) getSupportFragmentManager().findFragmentByTag("Fragment_TAG");
if (dialog != null) {
  dialog.dismiss();
}
Виктор Одиа
источник