Wie kann ich den Fragmentstatus beibehalten, wenn er zum Backstack hinzugefügt wird?

160

Ich habe eine Dummy-Aktivität geschrieben, die zwischen zwei Fragmenten wechselt. Wenn Sie von FragmentA zu FragmentB wechseln, wird FragmentA zum Backstack hinzugefügt. Wenn ich jedoch zu FragmentA zurückkehre (durch Zurückdrücken), wird ein völlig neues FragmentA erstellt und der Zustand, in dem es sich befand, geht verloren. Ich habe das Gefühl , ich nach der gleichen Sache wie mich diese Frage, aber ich habe ein vollständiges Codebeispiel , um Hilfe Wurzel aus der Ausgabe enthalten:

public class FooActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentA());
    transaction.commit();
  }

  public void nextFragment() {
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentB());
    transaction.addToBackStack(null);
    transaction.commit();
  }

  public static class FragmentA extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final View main = inflater.inflate(R.layout.main, container, false);
      main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
          ((FooActivity) getActivity()).nextFragment();
        }
      });
      return main;
    }

    @Override public void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save some state!
    }
  }

  public static class FragmentB extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.b, container, false);
    }
  }
}

Mit einigen hinzugefügten Protokollnachrichten:

07-05 14:28:59.722 D/OMG     ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG     ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG     ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG     ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG     ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG     ( 1260): FragmentA.onCreateView

Es ruft niemals FragmentA.onSaveInstanceState auf und erstellt ein neues FragmentA, wenn Sie zurückschlagen. Wenn ich jedoch auf FragmentA bin und den Bildschirm sperre, wird FragmentA.onSaveInstanceState aufgerufen. So seltsam ... irre ich mich, wenn ich erwarte, dass ein Fragment, das dem Backstack hinzugefügt wird, nicht neu erstellt werden muss? Folgendes sagen die Dokumente :

Wenn Sie dagegen beim Entfernen eines Fragments addToBackStack () aufrufen, wird das Fragment gestoppt und fortgesetzt, wenn der Benutzer zurück navigiert.

Eric
quelle
3
@ Jan-Henk Was ist mit Dingen, die geholt werden müssen? Zum Beispiel die Bildlaufposition von a ListView. Scheint viel zu viel Hoop-Jumping zu sein, um einen Scroll-Listener anzuhängen und eine Instanzvariable zu aktualisieren.
Jake Wharton
2
@JakeWharton Ich stimme zu, dass es einfacher sein sollte, aber soweit ich weiß, führt kein Weg daran vorbei, da onCreateView aufgerufen wird, wenn ein Fragment aus dem Backstack wiederhergestellt wird. Aber ich könnte mich irren :)
Jan-Henk
1
onCreate wird nicht aufgerufen. Anscheinend wird dieselbe Instanz wiederverwendet, aber onCreateView erneut aufgerufen? Lame. Ich denke, ich kann das Ergebnis von onCreateView einfach zwischenspeichern und die vorhandene Ansicht zurückgeben, wenn onCreateView erneut aufgerufen wird.
Eric
1
Genau das, wonach ich stundenlang gesucht habe. Können Sie posten, wie Sie dies mithilfe der Instanzvariablen erreicht haben?
Uma
1
Daher habe ich kürzlich meine eigene Implementierung in github.com/frostymarvelous/Folio gestartet und bin auf ein Problem gestoßen . Ich kann ungefähr 5 komplexe Seiten / Fragmente erstellen, bevor ich OOM-Abstürze bekomme. Das hat mich hierher geführt. Verstecken und Zeigen ist einfach nicht genug. Ansichten sind zu speicherlastig.
frostymarvelous

Antworten:

120

Wenn Sie vom Backstack zu einem Fragment zurückkehren, wird das Fragment nicht neu erstellt, sondern dieselbe Instanz erneut verwendet. Beginnen Sie mit onCreateView()dem Fragmentlebenszyklus (siehe Fragmentlebenszyklus) .

Wenn Sie also den Status speichern möchten, sollten Sie Instanzvariablen verwenden und sich nicht darauf verlassen onSaveInstanceState().

Jan-Henk
quelle
32
Die aktuelle Version der Dokumentation widerspricht dieser Behauptung. Das Flussdiagramm sagt, was Sie angeben, aber der Text im Hauptbereich der Seite sagt, dass onCreateView () nur aufgerufen wird, wenn das Fragment zum ersten Mal angezeigt wird: developer.android.com/guide/components/fragments.html Ich bekämpfe dies Problem jetzt, und ich sehe keine Methoden aufgerufen, wenn ein Fragment aus dem Backstack zurückgegeben wird. (Android 4.2)
Colin M.
10
Versucht, sein Verhalten zu protokollieren. OnCreateView () wird immer aufgerufen, wenn das Fragment angezeigt wird.
Princepiero
4
@ColinM. Irgendeine Lösung für das Problem?
Schneesturm
9
Das funktioniert bei mir nicht. Meine Instanzvariablen sind null, wenn ich zum Fragment zurückkehre! Wie kann ich den Staat retten?
Don Rhummy
5
Wenn wir also nicht auf eine gespeicherte Instanz weiterleiten sollten, wie sollten wir dann den Fragmentstatus und die Daten speichern?
Mahdi
80

Im Vergleich zu Apple UINavigationControllerund UIViewControllerGoogle schneidet Google in der Android-Softwarearchitektur nicht gut ab. Und das Dokument von Android über Fragmenthilft nicht viel.

Wenn Sie FragmentB über FragmentA eingeben, wird die vorhandene FragmentA-Instanz nicht zerstört. Wenn Sie in FragmentB auf Zurück drücken und zu FragmentA zurückkehren, wird keine neue FragmentA-Instanz erstellt. Die vorhandenen FragmentA-Instanzen onCreateView()werden aufgerufen.

Der Schlüssel ist, dass wir die Ansicht in FragmentAs nicht erneut aufblasen sollten onCreateView(), da wir die vorhandene FragmentA-Instanz verwenden. Wir müssen die rootView speichern und wiederverwenden.

Der folgende Code funktioniert gut. Es behält nicht nur den Fragmentstatus bei, sondern reduziert auch die RAM- und CPU-Last (da wir das Layout nur bei Bedarf aufblasen). Ich kann nicht glauben, dass Googles Beispielcode und Dokument es nie erwähnen, aber immer das Layout aufblasen .

Version 1 (Verwenden Sie nicht Version 1. Verwenden Sie Version 2)

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

------ Update am 3. Mai 2005: -------

Wie in den Kommentaren erwähnt, manchmal _rootView.getParent() null in onCreateView, was den Absturz verursacht. Version 2 entfernt _rootView in onDestroyView (), wie von dell116 vorgeschlagen. Getestet auf Android 4.0.3, 4.4.4, 5.1.0.

Version 2

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

WARNUNG!!!

Das ist ein HACK! Obwohl ich es in meiner App verwende, müssen Sie die Kommentare sorgfältig testen und lesen.

Vince Yuan
quelle
38
Es ist eine schlechte Idee, IMO einen Verweis auf die Rootview des gesamten Fragments zu halten. Wenn Sie einem Backstack kontinuierlich mehrere Fragmente hinzufügen und alle die Root-Ansicht (die einen ziemlich großen Speicherbedarf hat) enthalten, besteht eine hohe Wahrscheinlichkeit, dass Sie den OutOfMemoryError erhalten, da alle Fragmente die Root-View-Referenz und die GC-Überhöhung enthalten sammle es ein. Ich denke, der bessere Ansatz besteht darin, die Ansicht ständig aufzublasen (und das Android-System die Erstellung / Zerstörung der Ansicht übernehmen zu lassen) und onActivityCreated / onViewCreated zu überprüfen, ob Ihre Daten null sind. Wenn ja, laden Sie es, andernfalls setzen Sie die Daten auf Ansichten.
Traninho
15
Tu das nicht! Wenn die Ansichtshierarchie des Fragments erstellt wird, enthält sie einen internen Verweis auf die Aktivität, in der sich das Fragment zu diesem Zeitpunkt befand. Wenn eine Konfigurationsänderung auftritt, wird die Aktivität häufig neu erstellt. Durch die Wiederverwendung des alten Layouts bleibt diese Zombie-Aktivität zusammen mit den Objekten, auf die sie verweist, im Speicher. Eine solche Speicherverschwendung beeinträchtigt die Leistung und macht Ihre App zu einem Top-Kandidaten für die sofortige Beendigung, wenn sie nicht im Vordergrund steht.
Krylez
4
@ AllDayAmazing Dies ist ein guter Punkt. Um ehrlich zu sein, bin ich gerade sehr verwirrt. Kann jemand versuchen zu erklären, warum das Halten eines Verweises auf die Rootview eines Fragments nicht in Ordnung ist, aber das Halten eines Verweises nur für ein Kind von rootview (das ohnehin einen Verweis auf rootview hat) in Ordnung ist?
Traninho
2
Bleiben Sie davon fern, es sei denn, Sie möchten 5 Stunden damit verschwenden, herauszufinden, was Ihren Code nervt. Dann müssen Sie nur feststellen, dass dies die Ursache ist. Jetzt muss ich ein paar Sachen umgestalten, weil ich diesen Hack benutzt habe. Es ist viel besser, fragmentTransaction.add zu verwenden, wenn Sie die Benutzeroberfläche des Fragments in Kontakt halten möchten, wenn Sie eine andere in Sichtweite bringen (sogar oben). fragmentTransaction.replace () soll die Ansichten des Fragments zerstören ..... kämpfe nicht gegen das System.
Dell116
2
@VinceYuan - Ich habe mit der neuesten v7-appcompat-Bibliothek auf Android 5.1 getestet und dabei 6 Instanzen eines Fragments hinterlassen, das im FragmentManager meiner Aktivität hätte entfernt werden sollen. Selbst wenn der GC korrekt damit umgeht (was ich nicht glaube), führt dies zu einer unnötigen Belastung des Speichers für Ihre App und das Gerät im Allgemeinen. Durch die einfache Verwendung von .add () wird der gesamte Hacky-Code nicht mehr benötigt. Dies ist völlig im Widerspruch zu dem, was die Verwendung von FragmentTransaction.replace () eigentlich bedeuten sollte.
Dell116
53

Ich denke, es gibt einen alternativen Weg, um das zu erreichen, wonach Sie suchen. Ich sage nicht, dass es eine vollständige Lösung ist, aber es hat in meinem Fall den Zweck erfüllt.

Anstatt das Fragment zu ersetzen, habe ich gerade das Zielfragment hinzugefügt. Im Grunde werden Sie add()stattdessen die Methode verwendenreplace() .

Was ich sonst noch getan habe. Ich verstecke mein aktuelles Fragment und füge es dem Backstack hinzu.

Daher überlappt es neues Fragment mit dem aktuellen Fragment, ohne seine Ansicht zu zerstören (stellen Sie sicher, dass seine onDestroyView()Methode nicht aufgerufen wird. Fügen Sie es außerdem hinzubackstate ich den Vorteil, dass das Fragment wieder aufgenommen werden kann.

Hier ist der Code:

Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

AFAIK System ruft nur an onCreateView() wenn die Ansicht zerstört oder nicht erstellt wurde. Aber hier haben wir die Ansicht gespeichert, indem wir sie nicht aus dem Speicher entfernt haben. Es wird also keine neue Ansicht erstellt.

Und wenn Sie vom Zielfragment zurückkehren, wird das letzte angezeigt FragmentTransaction entfernte obere Fragment angezeigt, wodurch die oberste Ansicht (SourceFragment) über dem Bildschirm angezeigt wird.

KOMMENTAR: Wie gesagt, es ist keine vollständige Lösung, da die Ansicht des Quellfragments nicht entfernt wird und daher mehr Speicher als gewöhnlich belegt wird. Aber dennoch den Zweck erfüllen. Außerdem verwenden wir einen völlig anderen Mechanismus zum Ausblenden der Ansicht, anstatt sie zu ersetzen, was nicht traditionell ist.

Es geht also nicht wirklich darum, wie Sie den Status beibehalten, sondern darum, wie Sie die Ansicht beibehalten.

kaushal trivedi
quelle
In meinem Fall führt das Hinzufügen eines Fragments anstelle des Ersetzens zu Problemen bei der Verwendung von Polling oder einer anderen Art von Webanforderung im Fragment. Ich möchte diese Abfrage in Fragment A anhalten, wenn Fragment B hinzugefügt wird. Irgendeine Idee dazu?
Uma
Wie verwenden Sie Polling in FirstFragment? Sie müssen dies manuell tun, da beide Fragmente im Speicher verbleiben. Sie können also ihre Instanzen verwenden, um die erforderlichen Aktionen auszuführen. Dies ist der Hinweis. Generieren Sie ein Ereignis in der Hauptaktivität, das beim Hinzufügen des zweiten Fragments etwas bewirkt. Hoffe das wird helfen.
Kaushal Trivedi
1
Danke für den Hinweis =). Ich habe das getan. Aber ist dies der einzige Weg, dies zu tun? Und ein angemessener Weg? Auch wenn ich die Home-Taste drücke und die Anwendung erneut starte, werden alle Fragmente wieder aktiv. Angenommen, ich bin hier in Fragment B auf diese Weise. Activity A{Fragment A --> Fragment B}Wenn ich die Anwendung nach dem Drücken der Home-Taste erneut starte, werden beide Fragmente onResume()aufgerufen und beginnen daher mit der Abfrage. Wie kann ich das kontrollieren?
Uma
1
Leider können Sie nicht, System funktioniert nicht in normalem Verhalten auf diese Weise, es wird beide Fragmente als direktes Kind der Aktivität betrachten. Obwohl es dem Zweck diente, den Fragmentstatus aufrechtzuerhalten, werden andere normale Dinge sehr schwer zu verwalten. In letzter Zeit i entdeckte all diese Probleme, jetzt ist mein Vorschlag, nicht für diesen Weg zu gehen. Tut mir leid.
Kaushal Trivedi
1
Natürlich werde ich zum Schluss sagen, dass Sie diesen Ansatz erst wählen sollten, wenn Sie eine andere Lösung gefunden haben. Weil es schwierig ist, ihn zu verwalten.
Kaushal Trivedi
7

Ich würde eine sehr einfache Lösung vorschlagen.

Nehmen Sie die Referenzvariable Ansicht und legen Sie die Ansicht in OnCreateView fest. Überprüfen Sie, ob in dieser Variablen bereits eine Ansicht vorhanden ist, und geben Sie dieselbe Ansicht zurück.

   private View fragmentView;

   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        if (fragmentView != null) {
            return fragmentView;
        }
        View view = inflater.inflate(R.layout.yourfragment, container, false);
        fragmentView = view;
        return view;
    }
Mandeep Singh
quelle
1
Es besteht die Möglichkeit eines Speicherverlusts, wenn wir die Variable 'fragmentView' in onDestroy ()
Arun PM
@ArunPM Wie entferne ich fragmentView in onDestroy ()? if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }ist angemessen, um Speicher zu löschen?
Mehmet Gür
1
@ MehmetGür Ich benutze diese Lösung oft. Bis jetzt habe ich keinen Speicherverlustfehler erhalten. Sie können damit jedoch die ArunPM-Lösung verwenden, wenn Sie möchten. Ich denke, er sagt, dass fragmentView in der OnDestroy () -Methode auf null gesetzt werden soll.
Mandeep Singh
1
Ich verwende LeakCanary zum Erkennen von Speicherlecks und deren Problemen beim Auslösen von Lecks, als ich diese Methode befolgte. Aber wie @Mandeep Sigh im Kommentar erwähnt hat, können wir dieses Problem lösen, indem wir nullder fragmentView Variablen in der onDestroy()Methode zuweisen .
Arun PM
1
Wenn ein Fragment zerstört wird, wird meines Wissens die mit dem Fragment verknüpfte Ansicht gelöscht onDestroyView(). Diese Löschung findet für unsere Variable für die Sicherungsansicht (hier fragmentView ) nicht statt und führt zu einem Speicherverlust, wenn das Fragment zurück gestapelt / zerstört wird. Den gleichen Verweis finden Sie unter [Häufige Ursachen für Speicherverluste] ( square.github.io/leakcanary/fundamentals/… ) in der LeakCanery-Einführung.
Arun PM
6

Ich bin auf dieses Problem in einem Fragment gestoßen, das eine Karte enthält, die zu viele Setup-Details zum Speichern / Neuladen enthält. Meine Lösung bestand darin, dieses Fragment die ganze Zeit über aktiv zu halten (ähnlich wie bei @kaushal erwähnt).

Angenommen, Sie haben das aktuelle Fragment A und möchten Fragment B anzeigen. Fassen Sie die Konsequenzen zusammen:

  • replace () - entferne Fragment A und ersetze es durch Fragment B. Fragment A wird neu erstellt, sobald es wieder nach vorne gebracht wird
  • add () - (create and) füge ein Fragment B hinzu und es überlappt Fragment A, das im Hintergrund noch aktiv ist
  • remove () - kann verwendet werden, um Fragment B zu entfernen und zu A zurückzukehren. Fragment B wird neu erstellt, wenn es später aufgerufen wird

Wenn Sie also beide Fragmente "gespeichert" halten möchten, schalten Sie sie einfach mit hide () / show () um.

Vorteile : Einfache und einfache Methode, um mehrere Fragmente am Laufen zu halten
Nachteile : Sie verwenden viel mehr Speicher, um alle Fragmente am Laufen zu halten. Kann auf Probleme stoßen, z. B. beim Anzeigen vieler großer Bitmaps

Teo Inke
quelle
Können Sie mir bitte sagen, wann wir Fragment b entfernen und zu A zurückkehren? Welche Methode wird dann in Fragment A aufgerufen? Ich möchte etwas unternehmen, wenn wir das Fragment B entfernen.
Google
5

onSaveInstanceState() wird nur aufgerufen, wenn sich die Konfiguration ändert.

Seit dem Wechsel von einem Fragment zu einem anderen erfolgt keine Konfigurationsänderung, sodass kein Aufruf von vorhanden onSaveInstanceState()ist. Welcher Staat wird nicht gerettet? Können Sie angeben?

Wenn Sie Text in EditText eingeben, wird dieser automatisch gespeichert. Jedes UI-Element ohne ID ist das Element, dessen Ansichtsstatus nicht gespeichert werden soll.

user2779311
quelle
onSaveInstanceState()wird auch aufgerufen, wenn das System Aktivität zerstört, weil ihm Ressourcen fehlen.
Marcel Bro
0

Hier wird onSaveInstanceStatein fragment nicht aufgerufen, wenn Sie Fragment in den Backstack einfügen. Das Fragment Lebenszyklus in Backstack , wenn gestellt Beginn onCreateViewund Ende , onDestroyViewwährend onSaveInstanceStateheißt zwischen onDestroyViewund onDestroy. Meine Lösung besteht darin, eine Instanzvariable zu erstellen und in zu initiieren onCreate. Beispielcode:

private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     isDataLoading = false;
     // init list at once when create fragment
     listData = new ArrayList();
}

Und check es ein onActivityCreated:

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(isDataLoading){
         fetchData();
    }else{
         //get saved instance variable listData()
    }
}

private void fetchData(){
     // do fetch data into listData
}
Ling Boo
quelle
0
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
    {
        @Override
        public void onBackStackChanged()
        {
            if (getSupportFragmentManager().getBackStackEntryCount() == 0)
            {
                //setToolbarTitle("Main Activity");
            }
            else
            {
                Log.e("fragment_replace11111", "replace");
            }
        }
    });


YourActivity.java
@Override
public void onBackPressed()
{
 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.Fragment_content);
  if (fragment instanceof YourFragmentName)
    {
        fragmentReplace(new HomeFragment(),"Home Fragment");
        txt_toolbar_title.setText("Your Fragment");
    }
  else{
     super.onBackPressed();
   }
 }


public void fragmentReplace(Fragment fragment, String fragment_name)
{
    try
    {
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.Fragment_content, fragment, fragment_name);
        fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
        fragmentTransaction.addToBackStack(fragment_name);
        fragmentTransaction.commitAllowingStateLoss();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
Hardik Vasani
quelle
0

Mein Problem war ähnlich, aber ich habe mich überwunden, ohne das Fragment am Leben zu erhalten. Angenommen, Sie haben eine Aktivität mit zwei Fragmenten - F1 und F2. F1 wird anfangs gestartet und enthält beispielsweise einige Benutzerinformationen. Unter bestimmten Bedingungen wird F2 angezeigt, wenn der Benutzer aufgefordert wird, ein zusätzliches Attribut einzugeben - seine Telefonnummer. Als Nächstes möchten Sie, dass diese Telefonnummer auf F1 zurückkehrt und die Anmeldung abgeschlossen wird. Sie stellen jedoch fest, dass alle vorherigen Benutzerinformationen verloren gehen und Sie nicht über die vorherigen Daten verfügen. Das Fragment wird von Grund auf neu erstellt, und selbst wenn Sie diese Informationen im onSaveInstanceStateBundle gespeichert haben, wird null in zurückgegeben onActivityCreated.

Lösung: Speichern Sie die erforderlichen Informationen als Instanzvariable in der aufrufenden Aktivität. Übergeben Sie dann diese Instanzvariable an Ihr Fragment.

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    Bundle args = getArguments();

    // this will be null the first time F1 is created. 
    // it will be populated once you replace fragment and provide bundle data
    if (args != null) {
        if (args.get("your_info") != null) {
            // do what you want with restored information
        }
    }
}

Folgen Sie also meinem Beispiel: Bevor ich F2 anzeige, speichere ich Benutzerdaten mithilfe eines Rückrufs in der Instanzvariablen. Dann starte ich F2, der Benutzer gibt die Telefonnummer ein und drückt Speichern. Ich benutze einen anderen Rückruf in Tätigkeit, sammeln diese Informationen und ersetzen meine Fragment F1, diesmal es hat Bündel Daten , dass ich verwenden kann.

@Override
public void onPhoneAdded(String phone) {
        //replace fragment
        F1 f1 = new F1 ();
        Bundle args = new Bundle();
        yourInfo.setPhone(phone);
        args.putSerializable("you_info", yourInfo);
        f1.setArguments(args);

        getFragmentManager().beginTransaction()
                .replace(R.id.fragmentContainer, f1).addToBackStack(null).commit();

    }
}

Weitere Informationen zu Rückrufen finden Sie hier: https://developer.android.com/training/basics/fragments/communicating.html

Dodi
quelle
0

zuerst : Verwenden Sie einfach die Methode add anstelle der Methode replace der FragmentTransaction-Klasse. Dann müssen Sie secondFragment zum Stapel mit der Methode addToBackStack hinzufügen

zweite : auf der Rückseite klicken Sie popBackStackImmediate () aufrufen

Fragment sourceFragment = new SourceFragment ();
final Fragment secondFragment = new SecondFragment();
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.add(R.id.child_fragment_container, secondFragment );
ft.hide(sourceFragment );
ft.addToBackStack(NewsShow.class.getName());
ft.commit();
                                
((SecondFragment)secondFragment).backFragmentInstanceClick = new SecondFragment.backFragmentNewsResult()
{
        @Override
        public void backFragmentNewsResult()
        {                                    
            getChildFragmentManager().popBackStackImmediate();                                
        }
};
Milad Ahmadi
quelle
0

Ersetzen Sie ein Fragment mit folgendem Code:

Fragment fragment = new AddPaymentFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment, "Tag_AddPayment")
                .addToBackStack("Tag_AddPayment")
                .commit();

OnBackPressed () der Aktivität lautet:

  @Override
public void onBackPressed() {
    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 1) {

        fm.popBackStack();
    } else {


        finish();

    }
    Log.e("popping BACKSTRACK===> ",""+fm.getBackStackEntryCount());

}
Pratibha Sarode
quelle
0
Public void replaceFragment(Fragment mFragment, int id, String tag, boolean addToStack) {
        FragmentTransaction mTransaction = getSupportFragmentManager().beginTransaction();
        mTransaction.replace(id, mFragment);
        hideKeyboard();
        if (addToStack) {
            mTransaction.addToBackStack(tag);
        }
        mTransaction.commitAllowingStateLoss();
    }
replaceFragment(new Splash_Fragment(), R.id.container, null, false);
Hitesh Manwani
quelle
1
Vielen Dank für dieses Code-Snippet, das möglicherweise nur begrenzte und sofortige Hilfe bietet. Eine richtige Erklärung würde ihren langfristigen Wert erheblich verbessern, indem sie zeigt, warum dies eine gute Lösung für das Problem ist, und es für zukünftige Leser mit anderen, ähnlichen Fragen nützlicher machen. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, einschließlich der von Ihnen getroffenen Annahmen.
Machavity
0

Perfekte Lösung, die altes Fragment im Stapel findet und es lädt, wenn es im Stapel vorhanden ist.

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

benutze es wie

Fragment f = new YourFragment();
replaceFragment(f, null, boolean true, true); 
Khemraj
quelle