Jak zdobyć Fragment, aby sam się usunął, tj. Jego odpowiednik wykończenia ()?

230

Konwertuję aplikację do używania fragmentów za pomocą biblioteki kompatybilności. Obecnie mam szereg działań (ABCD), które łączą się ze sobą, D ma przycisk „OK”, który po naciśnięciu wywołania kończy się, a następnie przechodzi w pęcherzyki, onActivityResult()aby dodatkowo zniszczyć C i B.

W mojej poprzedniej wersji fragmentu Honycomb każda czynność jest efektywnie owijaniem fragmentów Af Bf Cf Df. Wszystkie działania są uruchamiane za pośrednictwem startActivityForResult()i onActivityResult()wewnątrz każdego fragmentu można z radością zadzwonićgetActivity().finish()

Problem, który mam, jest w mojej wersji Plaster miodu Mam tylko jedną aktywność, A, a fragmenty Bf, Cf, Df są ładowane za pomocą FragmentManager.

Nie rozumiem, co robić w Df po naciśnięciu „OK” w celu usunięcia fragmentów Df, Cf i Bf?

Próbowałem spowodować, że fragment sam wyskakuje ze stosu, ale spowodowało to wyjątek. onActivityResult()jest bezużyteczny, ponieważ nie załadowałem fragmentu za pomocąstartActivityForResult() .

Czy myślę o tym całkowicie niewłaściwie? Czy powinienem implementować jakiś detektor, który komunikuje się z fragmentem nadrzędnym lub działaniem, aby wykonać pop przy użyciu menedżera transakcji?

PJL
źródło
7
co z ((YourActivity) getActivity ()). onBackPressed ();
Viswanath Lekshmanan
@ViswanathLekshmanan twoja odpowiedź na komentarz jest dla mnie przydatna .. 1 Pozytywne zdanie ode mnie
Abhishek Singh
@AbhishekSingh Dobrze słyszeć :)
Viswanath Lekshmanan
@ViswanathLekshmanan :)
Abhishek Singh

Odpowiedzi:

62

Nie rozumiem, co robić w Df po naciśnięciu „OK” w celu usunięcia fragmentów Df, Cf i Bf?

Krok # 1: Niech Df powie D: „Cześć! Mamy OK, kliknij!” poprzez wywołanie metody albo na samym działaniu, albo na instancji interfejsu dostarczonej przez działanie.

Krok # 2: Niech D. usunie fragmenty za pośrednictwem FragmentManager.

Działalność hostingowa (D) to taka, która wie, jakie inne fragmenty znajdują się w aktywności (w porównaniu do bycia w innych działaniach). Dlatego zdarzenia wewnątrz fragmentu, które mogą wpływać na mieszankę fragmentów, powinny być propagowane do działania, które wykona odpowiednie ruchy aranżacyjne.

CommonsWare
źródło
Ale w mojej wersji Honeycomb nie ma D, to jest moje trudne. Jest po prostu aktywność A, która ładuje fragment Bf, która ładuje Cf, która ładuje Df za pomocą FragmentTransaction.
PJL
1
@PJL: Przepraszam, miałem na myśli A. To jest jeden powód, aby używać interfejsu nasłuchiwania, więc wiele działań może reagować na zdarzenie „mamy OK kliknięcie” z Df.
CommonsWare
1
Gdy aktualnie portuję, wywołałem metodę detektora z fragmentów metody onActivityResult Df do działania, po czym nazwałem popBackStack w FragmentManager. Powoduje to jednak wyjątek „IllegalStateException: Nie można wykonać tej akcji po onSaveInstanceState”. Wszelkie pomysły na to, jak to rozwiązać?
PJL
1
@DiegoPalomar: finish()powinno wystarczyć.
CommonsWare
1
@ user3364963: Minęło trochę czasu, odkąd to zbadałem, ale IIRC jest niszczony, gdy wyskakuje z tylnego stosu. Dodaj onDestroy()metodę do swojego fragmentu i sprawdź, czy zostanie wywołany.
CommonsWare
313

Chociaż może to nie być najlepsze podejście, najbliższy odpowiednik, jaki mogę wymyślić, działa, to z biblioteką wsparcia / kompatybilności

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();

lub

getActivity().getFragmentManager().beginTransaction().remove(this).commit();

Inaczej.

Ponadto możesz użyć backstacka i go pop. Pamiętaj jednak, że fragment może nie znajdować się w stosie wstecznym (w zależności od transakcji, która go tam dostała ...) lub może nie być ostatnim, który dostał się na stos, więc usunięcie stosu może usunąć niewłaściwy ...

Manfred Moser
źródło
11
Podczas gdy to podejście działa, jeśli używasz addToBackStack (null), pozostawi on obsługę przycisku Wstecz +1. Będziesz musiał nacisnąć go dwa razy.
user123321
34
„pop” fragment z FragmentManager.
user123321,
2
Próbowałem powyższej procedury, ale występuje ten błąd „wyjątek java-lang-nielegalny stan wyjątek-nie można wykonać-tej-akcji-po-zapisaniu”. Więc gdzie dokładnie muszę usunąć fragment
KK_07k11A0585
6
Ta odpowiedź jest złą praktyką i nie powinna podnosić głosów. Fragmenty nie powinny być tak świadome. Zabija wielokrotnego użytku, co jest fragmentem! Fragment powinien sygnalizować aktywność usunięcia go dowolną liczbą środków. Metoda interfejsu zwrotnego jest popularnym wyborem. developer.android.com/training/basics/fragments/…
colintheshots
2
@ManfredMoser Nie zgadzam się. To jest właśnie sedno pytania. Ma całą sekwencję fragmentów do usunięcia. Ten kod nie ma żadnych wartości zerowych ani sprawdza, czy działanie jest dołączone. Zepsuje produkcję, ponieważ zależy od zbyt wielu rzeczy, których fragment nie wie.
colintheshots
263

Możesz użyć poniższego podejścia, działa dobrze:

getActivity().getSupportFragmentManager().popBackStack();
Eric Yuan
źródło
44
Ta odpowiedź jest 10 razy lepsza niż zaakceptowana - od razu do rzeczy.
nikib3ro
50
Jest także 10 razy gorszy pod względem wzornictwa niż zaakceptowany. Fragment ma być małym „pomocnikiem” w działaniu i nigdy nie powinien mieć kontroli nad sobą lub innymi fragmentami
Patrick
6
Rozwiązanie jest nieprawidłowe, jak wskazał @avalancha. Spójrz na developer.android.com/guide/components/…
the_dark_destructor
2
Korzystam z tej metody onActivityResult i pojawia się błąd „Nie można wykonać tej akcji po onSaveInstanceState”. Jak mogę to rozwiązać?
Jayeshkumar Sojitra
1
popBackStack()to jedyne rozwiązanie, które działa, gdy chcesz usunąć tytuł paska akcji do poprzedniego stanu po usunięciu fragmentu. W przeciwnym razie nie musiałbym łączyć obu wysoko ocenianych rozwiązań z stackoverflow.com/questions/13472258/... i tego rozwiązania tutaj, aby zawsze ustawiać odpowiedni tytuł paska akcji po różnych przypadkach użycia. takie jak naciśnięcie wstecz, usuwanie, dodawanie zastępowania itd.
Bevor,
36

Powinieneś pozwolić Aktywności zajmować się dodawaniem i usuwaniem Fragmentów, jak mówi CommonsWare, używaj detektora. Oto przykład:

public class MyActivity extends FragmentActivity implements SuicidalFragmentListener {

    // onCreate etc

    @Override
    public void onFragmentSuicide(String tag) {
        // Check tag if you do this with more than one fragmen, then:
        getSupportFragmentManager().popBackStack();
    }
}

public interface SuicidalFragmentListener {
    void onFragmentSuicide(String tag);
}

public class MyFragment extends Fragment {

    // onCreateView etc

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
           suicideListener = (SuicidalFragmentListener) activity;
        } catch (ClassCastException e) {
           throw new RuntimeException(getActivity().getClass().getSimpleName() + " must implement the suicide listener to use this fragment", e);
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // Attach the close listener to whatever action on the fragment you want
        addSuicideTouchListener();
    }

    private void addSuicideTouchListener() {
        getView().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              suicideListener.onFragmentSuicide(getTag());
            }
        });
    }
}
Blundell
źródło
5
Samobójstwo w stylu emo? Co powiesz na „SelfClosing” lub „AutoClose” lub „SmartClose” (r)
DritanX
21
to się nie zamyka, to DYING FOREVER ;-(
Blundell,
3
Jest to znacznie czystsze podejście niż inne odpowiedzi. Aktywność tworzy i prezentuje fragment oraz powinna kontrolować jego cykl życia. Kiedy dzieje się coś, co wskazuje, że fragment nie powinien być już widoczny, powinien poinformować o tym Działanie i pozwolić, aby działanie je usunęło.
Christopher Pickslay
7
Technicznie rzecz biorąc, jeśli mamy mieć aktywność zabijającą fragment, to fragment nie jest samobójczy. Działalność jest mordercza.
Casey Murray,
17

W Activity / AppCompatActivity:

@Override
public void onBackPressed() {
    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
        // if you want to handle DrawerLayout
        mDrawerLayout.closeDrawer(GravityCompat.START);
    } else {
        if (getFragmentManager().getBackStackEntryCount() == 0) {
            super.onBackPressed();
        } else {
            getFragmentManager().popBackStack();
        }
    }
}

a następnie wywołaj fragment:

getActivity().onBackPressed();

lub jak podano w innych odpowiedziach, nazwij to we fragmencie:

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
Codeversed
źródło
5

Jeśli używasz nowego komponentu nawigacyjnego, jest to proste jak

findNavController().popBackStack()

Wykona za Ciebie całą FragmentTransaction.

Gastón Saillén
źródło
4

Sprawdź, czy Twoje potrzeby zostały zaspokojone przez DialogFragment. DialogFragment ma metodę DISISS (). Moim zdaniem znacznie czystszy.

Vasudev
źródło
3

Tworzę do tego prostą metodę

popBackStack(getSupportFragmentManager());

Następnie umieść go w mojej klasie ActivityUtils

public static void popBackStack(FragmentManager manager){
        FragmentManager.BackStackEntry first = manager.getBackStackEntryAt(0);
        manager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

Działa świetnie, baw się dobrze!

EliaszKubala
źródło
2
Nie rozumiem celu twojej metody. Oryginalny popBackStack wydaje się całkowicie odpowiedni.
Niesamowity
1

OnCreate:

//Add comment fragment
            container = FindViewById<FrameLayout>(Resource.Id.frmAttachPicture);
            mPictureFragment = new fmtAttachPicture();

            var trans = SupportFragmentManager.BeginTransaction();
            trans.Add(container.Id, mPictureFragment, "fmtPicture");
            trans.Show(mPictureFragment); trans.Commit();

W ten sposób ukrywam fragment w zdarzeniu kliknięcia 1

//Close fragment
    var trans = SupportFragmentManager.BeginTransaction();
    trans.Hide(mPictureFragment);
    trans.AddToBackStack(null);
    trans.Commit();

Następnie pokazuje to z powrotem w przypadku zdarzenia 2

var trans = SupportFragmentManager.BeginTransaction();
            trans.Show(mPictureFragment); trans.Commit();
CodeSi
źródło
1

Jeśli chcesz cofnąć się z czwartego fragmentu w historii plecaka do pierwszego, użyj tagów !!!

Kiedy dodajesz pierwszy fragment, powinieneś użyć czegoś takiego:

getFragmentManager.beginTransaction.addToBackStack("A").add(R.id.container, FragmentA).commit() 

lub

getFragmentManager.beginTransaction.addToBackStack("A").replace(R.id.container, FragmentA).commit()

A kiedy chcesz pokazać Fragmenty B, C i D, użyj tego:

getFragmentManager.beginTransaction.addToBackStack("B").replace(R.id.container, FragmentB, "B").commit()

i inne litery ....

Aby powrócić do FragmentA, po prostu zadzwoń popBackStack(0, "A"), tak, użyj flagi podanej podczas dodawania i zwróć uwagę, że musi to być ta sama flaga w poleceniuaddToBackStack() , a nie ta używana w poleceniu zamień lub dodaj.

Nie ma za co ;)

Diego Ramírez Vásquez
źródło
Testowałem „popBackStack (0,„ A ”)” i moja aplikacja wraca do fragmentu A, ale chcę, aby tylko ten fragment został usunięty z Back Stack ... Jak mogę usunąć fragment ze Stack bez wyświetlania go na ekranie?
KryNaC
1

Aby zamknąć fragment w tym samym fragmencie

getActivity().onBackPressed();
Dino Sunny
źródło
-9

Dlaczego nie tylko:

getActivity (). finish ();

Będzie
źródło
4
To zakończy całą aktywność, a nie tylko fragment.
Murtadha S.