Wie kann man ein Fragment dazu bringen, sich selbst zu entfernen, dh das Äquivalent von finish ()?


230

Ich konvertiere eine App, um Fragmente mithilfe der Kompatibilitätsbibliothek zu verwenden. Derzeit habe ich eine Reihe von Aktivitäten (ABCD), die aneinander gekettet sind. D hat eine Schaltfläche 'OK', die beim Drücken der Anrufe beendet wird und dann durchsprudelt, onActivityResult()um C und B zusätzlich zu zerstören.

Für meine Version vor Honycomb-Fragmenten ist jede Aktivität effektiv ein Wrapper für Fragmente Af Bf Cf Df. Alle Aktivitäten werden über startActivityForResult()und onActivityResult()innerhalb jedes der Fragmente gestartet, die gerne aufgerufen werden könnengetActivity().finish()

Das Problem, das ich habe, ist, dass ich in meiner Honeycomb-Version nur eine Aktivität habe, A, und Fragmente Bf, Cf, Df werden mit dem geladen FragmentManager.

Was ich nicht verstehe, ist, was in Df zu tun ist, wenn 'OK' gedrückt wird, um die Fragmente Df, Cf und Bf zu entfernen?

Ich habe versucht, das Fragment vom Stapel zu entfernen, aber dies führte zu einer Ausnahme. onActivityResult()ist nutzlos, weil ich das Fragment nicht mit geladen habe startActivityForResult().

Denke ich völlig falsch darüber nach? Sollte ich eine Art Listener implementieren, der entweder mit dem übergeordneten Fragment oder der Aktivität kommuniziert, um den Pop über den Transaktionsmanager auszuführen?


7
was ist mit ((YourActivity) getActivity ()). onBackPressed ();
Viswanath Lekshmanan

@ ViswanathLekshmanan Ihre Kommentarantwort ist nützlich für mich .. 1 Upvote von mir
Abhishek Singh

@ AbhishekSingh Gut zu hören :)
Viswanath Lekshmanan

@ ViswanathLekshmanan :)
Abhishek Singh

Antworten:


62

Was ich nicht verstehe, ist, was in Df zu tun ist, wenn 'OK' gedrückt wird, um die Fragmente Df, Cf und Bf zu entfernen?

Schritt 1: Lassen Sie Df D sagen "yo! Wir haben den OK-Klick!" über den Aufruf einer Methode, entweder für die Aktivität selbst oder für eine von der Aktivität bereitgestellte Schnittstelleninstanz.

Schritt 2: Lassen Sie D die Fragmente über entfernen FragmentManager.

Die Hosting-Aktivität (D) ist diejenige, die weiß, welche anderen Fragmente in der Aktivität enthalten sind (im Gegensatz zu anderen Aktivitäten). Daher sollten In-Fragment-Ereignisse, die sich auf den Fragment-Mix auswirken könnten, an die Aktivität weitergegeben werden, wodurch die entsprechenden Orchestrierungsschritte ausgeführt werden.


Aber in meiner Honeycomb-Version gibt es kein D, das ist meine Schwierigkeit. Es gibt einfach eine Aktivität A, die das Fragment Bf lädt, die Cf lädt, die Df mit FragmentTransaction lädt.
PJL

1
@PJL: Entschuldigung, ich meinte A. Dies ist ein Grund, eine Listener-Oberfläche zu verwenden, damit mehrere Aktivitäten auf das Ereignis "Wir haben den OK-Klick" von Df reagieren können.
CommonsWare

1
Während ich gerade portiere, habe ich eine Listener-Methode aus Fragmenten der onActivityResult-Methode von Df in die Aktivität aufgerufen, woraufhin ich dann popBackStack auf dem FragmentManager aufgerufen habe. Dies führt jedoch zu einer Ausnahme "IllegalStateException: Kann diese Aktion nach onSaveInstanceState nicht ausführen". Irgendwelche Ideen, wie ich dies überwinden kann?
PJL

1
@DiegoPalomar: finish()sollte ausreichen.
CommonsWare

1
@ user3364963: Es ist eine Weile her, seit ich das untersucht habe, aber IIRC, es wird zerstört, wenn es vom Backstack geknallt wird. Fügen Sie onDestroy()Ihrem Fragment eine Methode hinzu und prüfen Sie, ob sie aufgerufen wird.
CommonsWare

313

Obwohl dies möglicherweise nicht der beste Ansatz ist, funktioniert dies am besten mit der Support- / Kompatibilitätsbibliothek

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

oder

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

Andernfalls.

Außerdem können Sie den Backstack verwenden und einfügen. Beachten Sie jedoch, dass sich das Fragment möglicherweise nicht auf dem Backstack befindet (abhängig von der Fragmenttransaktion, die es dort erhalten hat ..) oder dass es möglicherweise nicht das letzte ist, das auf den Stapel gelangt ist, sodass durch das Aufspringen des Stapels das falsche entfernt werden kann ...


11
Während dieser Ansatz funktioniert, bleibt bei Verwendung von addToBackStack (null) der Back-Button-Handler +1 übrig. Sie müssen es also zweimal drücken.
user123321

34
"Pop" das Fragment aus dem FragmentManager.
user123321

2
Ich habe das obige Verfahren ausprobiert, aber es gibt den Fehler "java-lang-illegalstateexception-can-not-this-action-after-onsaveinstance". Also, wo genau muss ich das Fragment entfernen
KK_07k11A0585

6
Diese Antwort ist eine schlechte Praxis und sollte keine Stimmen bekommen. Fragmente sollen so nicht selbstbewusst sein. Es tötet die Wiederverwendbarkeit, worum es bei Fragmenten geht! Das Fragment sollte die Aktivität signalisieren, es mit einer beliebigen Anzahl von Mitteln zu entfernen. Die Callback-Schnittstellenmethode ist eine beliebte Wahl. developer.android.com/training/basics/fragments/…
Colintheshots

2
@ManfredMoser Ich bin anderer Meinung. Dies ist sehr der Punkt der Frage. Er muss eine ganze Folge von Fragmenten entfernen. Dieser Code enthält keine Nullprüfungen oder Überprüfungen, ob die Aktivität angehängt ist. Es wird in der Produktion brechen, weil es von zu vielen Dingen abhängt, die ein Fragment nicht weiß.
Colintheshots

263

Sie können den folgenden Ansatz verwenden, es funktioniert gut:

getActivity().getSupportFragmentManager().popBackStack();

44
Diese Antwort ist zehnmal besser als die akzeptierte - direkt auf den Punkt.
Nikib3ro

50
Es ist auch 10 Mal schlechter in Bezug auf Design als das akzeptierte. Ein Fragment soll ein kleiner "Helfer" für eine Aktivität sein und niemals die Kontrolle über sich selbst oder andere Fragmente haben
Patrick

6
Die Lösung ist nicht korrekt, wie @avalancha hervorhob.
Werfen

2
Ich verwende diese Methode onActivityResult und erhalte die Fehlermeldung "Diese Aktion kann nach onSaveInstanceState nicht ausgeführt werden". Wie kann ich das Problem beheben?
Jayeshkumar Sojitra

1
popBackStack()ist die einzige Lösung, die funktioniert, wenn Sie den Titel der Aktionsleiste nach dem Löschen des Fragments auf den vorherigen Status zurücksetzen möchten. Andernfalls müsste ich nicht die beiden hoch bewerteten Lösungen von stackoverflow.com/questions/13472258/… und diese Lösung hier kombinieren, um nach verschiedenen Anwendungsfällen immer den richtigen Titel in der Aktionsleiste festzulegen . wie Zurückdrücken, Löschen, Hinzufügen Ersetzen und so weiter.
Bevor

36

Sie sollten die Aktivität mit dem Hinzufügen und Entfernen von Fragmenten befassen, wie CommonsWare sagt, und einen Listener verwenden. Hier ist ein Beispiel:

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

5
Selbstmord viel Emo-Stil? Wie wäre es mit "SelfClosing" oder "AutoClose" oder "SmartClose" (r)
DritanX

21
es schließt nicht, es stirbt für immer ;-(
Blundell

3
Dies ist ein viel saubererer Ansatz als die anderen Antworten. Die Aktivität erstellt und präsentiert das Fragment und sollte seinen Lebenszyklus steuern. Wenn etwas passiert, das anzeigt, dass das Fragment nicht mehr angezeigt werden soll, sollte es der Aktivität dies mitteilen und die Aktivität es entfernen lassen.
Christopher Pickslay

7
Technisch gesehen ist das Fragment nicht selbstmörderisch, wenn die Aktivität das Fragment töten soll. Die Aktivität ist tödlich.
Casey Murray

17

In der Aktivität / 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();
        }
    }
}

und rufen Sie dann das Fragment auf:

getActivity().onBackPressed();

oder wie in anderen Antworten angegeben, nennen Sie dies im Fragment:

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

5

Wenn Sie die neue Navigationskomponente verwenden, ist dies einfach

findNavController().popBackStack()

Es erledigt die gesamte FragmentTransaction für Sie.


4

Überprüfen Sie, ob Ihre Anforderungen von einem DialogFragment erfüllt werden. DialogFragment hat eine Entlassung () -Methode. Meiner Meinung nach viel sauberer.


3

Ich erstelle dafür eine einfache Methode

popBackStack(getSupportFragmentManager());

Dann platziere es in meiner ActivityUtils-Klasse

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

Es funktioniert super, viel Spaß!


2
Ich verstehe den Zweck Ihrer Methode nicht. Der ursprüngliche popBackStack scheint völlig ausreichend zu sein.
Der unglaubliche

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

So verstecke ich das Fragment in Klickereignis 1

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

Zeigt es dann zurück in Ereignis 2

var trans = SupportFragmentManager.BeginTransaction();
            trans.Show(mPictureFragment); trans.Commit();

1

Wenn Sie vom vierten Fragment im Backstack-Verlauf zum ersten zurückkehren müssen, verwenden Sie Tags !!!

Wenn Sie das erste Fragment hinzufügen, sollten Sie Folgendes verwenden:

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

oder

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

Und wenn Sie die Fragmente B, C und D zeigen möchten, verwenden Sie Folgendes:

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

und andere Briefe ....

Um zu FragmentA zurückzukehren, rufen Sie einfach an popBackStack(0, "A"), ja, verwenden Sie das Flag, das Sie beim Hinzufügen angegeben haben, und beachten Sie, dass es dasselbe Flag im Befehl sein muss addToBackStack(), nicht das, das beim Ersetzen oder Hinzufügen des Befehls verwendet wird.

Bitte ;)


Ich habe 'popBackStack (0, "A")' getestet und meine App kehrt zu Fragment A zurück, aber ich möchte, dass nur dieses Fragment aus dem Back Stack entfernt wird ... Wie kann ich ein Fragment aus dem Stack entfernen, ohne es auf dem Bildschirm anzuzeigen?
KryNaC

1

So schließen Sie ein Fragment im selben Fragment

getActivity().onBackPressed();

-9

Warum nicht einfach:

getActivity (). finish ();


4
Dadurch wird die gesamte Aktivität beendet, nicht nur das Fragment.
Murtadha S.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.