Jak poprawnie odrzucić DialogFragment?

121

Dokumentacja mówi tak dla dismiss()metody z Dialogklasy:

Zamknij to okno dialogowe, usuwając je z ekranu. Tę metodę można bezpiecznie wywołać z dowolnego wątku. Zauważ, że nie powinieneś nadpisywać tej metody, aby wykonać czyszczenie, gdy okno dialogowe jest zamknięte, zamiast tego zaimplementuj ją w onStop().

W moim kodzie wszystko, co robię, to getDialog().dismiss()odwoływanie się. Ale nie robię nic innego ani nawet nie używam onStop(). Więc pytam dokładnie, jak poprawnie odrzucić a, DialogFragmentaby uniknąć wycieków pamięci itp.

Andy
źródło

Odpowiedzi:

197

tl; dr: poprawnym sposobem zamknięcia a DialogFragmentjest użycie dismiss() bezpośrednio w DialogFragment .


Szczegóły : dokumentacja stanów DialogFragment

Sterowanie oknem dialogowym (decydowanie, kiedy je pokazać, ukryć, zamknąć) powinno odbywać się za pośrednictwem interfejsu API tutaj, a nie za pomocą bezpośrednich wywołań w oknie dialogowym.

Dlatego nie powinieneś używać getDialog().dismiss(), ponieważ wywołałoby dismiss() to okno dialogowe . Zamiast tego należy użyć samej dismiss()metody DialogFragment:

publiczne void odrzucić ()

Odrzuć fragment i jego dialog. Jeśli fragment został dodany do tylnego stosu, wszystkie stany tylnego stosu do tego wpisu włącznie zostaną usunięte. W przeciwnym razie zostanie zatwierdzona nowa transakcja w celu usunięcia fragmentu.

Jak widać, zajmuje się to nie tylko zamknięciem okna dialogowego, ale także obsługą transakcji fragmentowych zaangażowanych w proces.

Musisz użyć tylko onStopwtedy, gdy jawnie utworzyłeś jakiekolwiek zasoby, które wymagają ręcznego czyszczenia (zamykanie plików, zamykanie kursorów itp.). Nawet wtedy zastąpiłbym onStopDialogFragment zamiast onStoppodstawowego Dialog.

Heinzi
źródło
1
@ScootrNova: Nie powinno, prawdopodobnie gdzie indziej masz błąd. Jak tworzysz ten fragment?
Heinzi
protected void showDialogFragment(final DialogFragment fragment) {final FragmentTransaction fTransaction = getSupportFragmentManager().beginTransaction(); fTransaction.addToBackStack(null); fragment.show(fTransaction, "dialog");} Przepraszam za paskudną jedną wkładkę! Ale tak, możesz mieć rację, więc na razie napisałem inny sposób na zamknięcie moich DialogFragments. Sposób, w jaki je odrzucałem za pomocą metody dismiss (), polegał na znalezieniu fragmentu po tagu, a następnie uruchomieniu na nim disciss (), jeśli nie był pusty. Aha i tak, newumieszczam fragment tuż przed przekazaniem go do tej metody.
Charles Madere
2
@ScootrNova: Hmm, nie widzę w tym nic złego - z drugiej strony nigdy nie korzystałem z biblioteki kompatybilności, więc nie mogę być tego pewien. Może warto byłoby stworzyć minimalny, samodzielny przykład i zacząć nowe pytanie na ten temat.
Heinzi
@CharlesMadere w tamtych czasach, czy znalazłeś rozwiązanie?
JCarlosR
Przepraszam @JCarlos, to było lata temu, nie jestem pewien.
Charles Madere
76

Myślę, że lepszym sposobem na zamknięcie DialogFragmentjest:

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

W ten sposób nie musisz mieć odniesienia do pliku DialogFragmenti możesz go zamknąć z dowolnego miejsca.

Terel
źródło
7

Dlaczego nie spróbujesz użyć tylko tego kodu:

dismiss();

Jeśli chcesz samodzielnie odrzucić fragment okna dialogowego. Możesz po prostu umieścić ten kod wewnątrz fragmentu okna dialogowego, w którym chcesz zamknąć okno dialogowe.

Na przykład:

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

Spowoduje to zamknięcie ostatniego fragmentu okna dialogowego, który jest wyświetlany na ekranie.

Mam nadzieję, że to pomoże.

Shiva Tiwari
źródło
nie działa cały czas
Mahmoud Heretani
5

Głosowałem za odpowiedzią Terela. Chciałem tylko opublikować to dla wszystkich użytkowników Kotlin:

supportFragmentManager.findFragmentByTag(TAG_DIALOG)?.let {
    (it as DialogFragment).dismiss()
}
JustinMorris
źródło
Prosty kod działa ciężko. Dzięki za aktualizację!
Ayush Katuwal
4

Odpowiedź Kotlin Version of Terel

(fragmentManager.findFragmentByTag(TAG) as? DialogFragment)?.dismiss()
Phil
źródło
1

Należy odrzucić cię Dialogw onPause()tak zastąpić.

Również przed zamknięciem możesz sprawdzić nulli wyświetla się jak poniżej fragment:

@Override
protected void onPause() {
    super.onPause();
    if (dialog != null && dialog.isShowing()) {
        dialog.dismiss();
    }
}
Venky
źródło
napisał już, że robi odciśniecie () i chodzi o DialogFragment.
Paresh Mayani
Myślę, że to działa zarówno dla Dialog, jak i DialogFragments @PareshMayani
Venky
2
Uważam, że @PareshMayani ma rację Venky. Samouczek na DialogFragmentGoogle w ogóle nie pokazuje onPause()używanej metody. Ale myślę, że rozumiem, co robisz. O co chodzi, jeśli użytkownik nie dzwoni onPause(). To wtedy, gdy system wie, że fragment jest wywoływany. A kiedy, powiedzmy, że użytkownik anuluje subskrypcję. Jaki jest lepszy sposób na zamknięcie tego w takim przypadku?
Andy
1

W innych odpowiedziach znajdują się odniesienia do oficjalnych dokumentów ( DialogFragment Reference ), ale nie ma wzmianki o podanym tam przykładzie:

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

Spowoduje to usunięcie aktualnie wyświetlanego okna dialogowego, utworzenie nowego DialogFragment z argumentem i wyświetlenie go jako nowego stanu na tylnym stosie. Po wyskakiwaniu transakcji bieżący DialogFragment i jego okno dialogowe zostaną zniszczone, a poprzednia (jeśli istnieje) zostanie ponownie pokazana. Zauważ, że w tym przypadku DialogFragment zajmie się popping transakcją Dialogu, która jest odrzucana oddzielnie od niej.

Na moje potrzeby zmieniłem to na:

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

MyDialogFragment fragment = new MyDialogFragment();
fragment.show(manager, TAG);
Maksim Ivanov
źródło
1

Dodanie do innych odpowiedzi, gdy DialogFragmentwywołanie jest pełnoekranowe dismiss(), nie spowoduje wyskoczenia DialogFragment z zaplecza fragmentu. Sposób obejścia problemu polega na wywołaniu onBackPressed()działania rodzica.

Coś takiego:

CustomDialogFragment.kt

closeButton.onClick {
    requireActivity().onBackPressed()
}
mikehc
źródło
Oszczędzaj dzień, wielkie dzięki
Mahmoud Heretani
0

Po prostu wywołaj odrzucenie () z fragmentu, który chcesz odrzucić.

imageView3.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            dismiss();
        }
    });
VIVEK CHOUDHARY
źródło
0

Zauważyłem, że gdy mój fragment został zdefiniowany na wykresie nawigacyjnym za pomocą <fragment>tagu (dla pełnoekranowego fragmentu dialogowego), fragment okna dialogowego nie został usunięty za pomocą dismiss()polecenia. Zamiast tego musiałem zdjąć tylny stos:

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

Jeśli jednak ten sam fragment okna dialogowego został zdefiniowany na wykresie nawigacyjnym ze <dialog>znacznikiem, dismiss()działa dobrze.

jon
źródło
0
CustomFragment dialog = (CustomDataFragment) getSupportFragmentManager().findFragmentByTag("Fragment_TAG");
if (dialog != null) {
  dialog.dismiss();
}
Victor Odiah
źródło