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.
ListView
. Scheint viel zu viel Hoop-Jumping zu sein, um einen Scroll-Listener anzuhängen und eine Instanzvariable zu aktualisieren.Antworten:
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()
.quelle
Im Vergleich zu Apple
UINavigationController
undUIViewController
Google schneidet Google in der Android-Softwarearchitektur nicht gut ab. Und das Dokument von Android überFragment
hilft 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)
------ Update am 3. Mai 2005: -------
Wie in den Kommentaren erwähnt, manchmal
_rootView.getParent()
null inonCreateView
, 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
WARNUNG!!!
Das ist ein HACK! Obwohl ich es in meiner App verwende, müssen Sie die Kommentare sorgfältig testen und lesen.
quelle
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:
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.
quelle
Activity A{Fragment A --> Fragment B}
Wenn ich die Anwendung nach dem Drücken der Home-Taste erneut starte, werden beide FragmenteonResume()
aufgerufen und beginnen daher mit der Abfrage. Wie kann ich das kontrollieren?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.
quelle
if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }
ist angemessen, um Speicher zu löschen?null
derfragmentView
Variablen in deronDestroy()
Methode zuweisen .onDestroyView()
. Diese Löschung findet für unsere Variable für die Sicherungsansicht (hierfragmentView
) 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.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:
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
quelle
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.
quelle
onSaveInstanceState()
wird auch aufgerufen, wenn das System Aktivität zerstört, weil ihm Ressourcen fehlen.Hier wird
onSaveInstanceState
in fragment nicht aufgerufen, wenn Sie Fragment in den Backstack einfügen. Das Fragment Lebenszyklus in Backstack , wenn gestellt BeginnonCreateView
und Ende ,onDestroyView
währendonSaveInstanceState
heißt zwischenonDestroyView
undonDestroy
. Meine Lösung besteht darin, eine Instanzvariable zu erstellen und in zu initiierenonCreate
. Beispielcode:Und check es ein
onActivityCreated
:quelle
quelle
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
onSaveInstanceState
Bundle gespeichert haben, wird null in zurückgegebenonActivityCreated
.Lösung: Speichern Sie die erforderlichen Informationen als Instanzvariable in der aufrufenden Aktivität. Übergeben Sie dann diese Instanzvariable an Ihr Fragment.
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.
Weitere Informationen zu Rückrufen finden Sie hier: https://developer.android.com/training/basics/fragments/communicating.html
quelle
quelle
Ersetzen Sie ein Fragment mit folgendem Code:
OnBackPressed () der Aktivität lautet:
quelle
quelle
Perfekte Lösung, die altes Fragment im Stapel findet und es lädt, wenn es im Stapel vorhanden ist.
benutze es wie
quelle