Ich habe Probleme damit, dass meine Fragmente über die Activity
, die die verwendet FragmentPagerAdapter
, als Hilfsklasse miteinander kommunizieren , die die Verwaltung von Registerkarten und alle Details zum Verbinden von a ViewPager
mit zugeordneten implementiert TabHost
. Ich habe FragmentPagerAdapter
genau so implementiert, wie es vom Android-Beispielprojekt Support4Demos bereitgestellt wird .
Die Hauptfrage ist, wie ich ein bestimmtes Fragment erhalten kann, FragmentManager
wenn ich weder ID noch Tag habe. FragmentPagerAdapter
erstellt die Fragmente und generiert automatisch die ID und Tags.
android
android-fragments
fragmentpageradapter
Ismar Slomic
quelle
quelle
Antworten:
Zusammenfassung des Problems
Hinweis: In dieser Antwort werde ich auf
FragmentPagerAdapter
den Quellcode verweisen . Die allgemeine Lösung sollte aber auch für geltenFragmentStatePagerAdapter
.Wenn Sie dies lesen Sie wahrscheinlich bereits wissen , dass
FragmentPagerAdapter
/FragmentStatePagerAdapter
sollte schaffenFragments
für IhreViewPager
, sondern auf Aktivität Erholung (ob von einem Gerät Rotation oder dem System zu töten Ihre App wieder Speicher) dieseFragments
werden nicht wieder angelegt, sondern ihre Instanzen aus demFragmentManager
. Sagen Sie nun, dass SieActivity
einen Verweis auf dieseFragments
benötigen, um daran arbeiten zu können. Sie haben keinid
odertag
für diese erstellt,Fragments
weil SieFragmentPagerAdapter
sie intern festlegen . Das Problem ist also, wie man ohne diese Informationen einen Verweis auf sie erhält ...Problem mit aktuellen Lösungen: Verlassen Sie sich auf internen Code
Viele der Lösungen, die ich für diese und ähnliche Fragen gesehen habe, basieren darauf, einen Verweis auf das Vorhandene zu erhalten
Fragment
,FragmentManager.findFragmentByTag()
indem das intern erstellte Tag"android:switcher:" + viewId + ":" + id
aufgerufen und nachgeahmt wird : . Das Problem dabei ist, dass Sie sich auf internen Quellcode verlassen, der bekanntlich nicht für immer gleich bleibt. Die Android-Ingenieure bei Google könnten leicht entscheiden, dietag
Struktur zu ändern, die Ihren Code beschädigen würde, sodass Sie keinen Verweis auf den vorhandenen finden könnenFragments
.Alternative Lösung ohne interne
tag
Hier ist ein einfaches Beispiel dafür, wie Sie einen Verweis auf das
Fragments
zurückgegebene erhaltenFragmentPagerAdapter
, das nicht von der internentags
Menge auf dem abhängtFragments
. Der Schlüssel besteht darininstantiateItem()
, Referenzen dort zu überschreiben und zu speichern, anstatt ingetItem()
.oder wenn Sie arbeiten lieber mit
tags
statt Klasse Membervariablen / Verweis auf dieFragments
Sie kann auch den greifentags
Satz vonFragmentPagerAdapter
auf die gleiche Weise: HINWEIS: dies gilt nicht ,FragmentStatePagerAdapter
da es nicht gesetzt ist ,tags
wenn seine ErstellungFragments
.Beachten Sie, dass diese Methode NICHT auf der Nachahmung des internen
tag
Satzes durch beruhtFragmentPagerAdapter
und stattdessen geeignete APIs zum Abrufen verwendet. Auf diese Weise sind Sie auch dann sicher, wenn Sietag
Änderungen in zukünftigen Versionen von vornehmenSupportLibrary
.Vergessen Sie nicht, dass das Design
Activity
, an demFragments
Sie arbeiten möchten , je nach Design möglicherweise noch vorhanden ist oder nicht. Sie müssen dies berücksichtigen, indem Sienull
Überprüfungen durchführen, bevor Sie Ihre Referenzen verwenden.Wenn Sie stattdessen mit arbeiten
FragmentStatePagerAdapter
, möchten Sie keine harten Verweise auf Ihre behalten,Fragments
da Sie möglicherweise viele davon haben und harte Verweise sie unnötigerweise im Speicher behalten würden. Speichern Sie stattdessen dieFragment
Referenzen inWeakReference
Variablen anstelle von Standardreferenzen. So was:quelle
instantiateItem
. der richtige Weg , dies zu tun ist, riefinstantiateItem
inonCreate
Methode Ihrer Aktivität von umgebenstartUpdate
undfinishUpdate
. Siehe meine Antwort für DetailsIch habe eine Antwort auf meine Frage gefunden, die auf dem folgenden Beitrag basiert: Wiederverwenden von Fragmenten in einem Fragmentpageradapter
Einige Dinge, die ich gelernt habe:
getItem(int position)
in derFragmentPagerAdapter
ist ziemlich irreführend Name dessen, was diese Methode tatsächlich tut. Es werden neue Fragmente erstellt und keine vorhandenen zurückgegeben. In diesem Sinne sollte die Methode in etwas wiecreateItem(int position)
im Android SDK umbenannt werden. Diese Methode hilft uns also nicht, Fragmente zu erhalten.FragmentPagerAdapter
und in diesem Sinne keinen Verweis auf die Fragmente oder deren Tags haben. Wenn Sie jedoch ein Fragment-Tag haben, können Sie den Verweis darauf einfachFragmentManager
durch Aufrufen abrufenfindFragmentByTag()
. Wir brauchen eine Möglichkeit, das Tag eines Fragments an einer bestimmten Seitenposition herauszufinden.Lösung
Fügen Sie Ihrer Klasse die folgende Hilfsmethode hinzu, um das Fragment-Tag abzurufen und an die
findFragmentByTag()
Methode zu senden .HINWEIS! Dies ist die identische Methode,
FragmentPagerAdapter
die beim Erstellen neuer Fragmente verwendet wird. Siehe diesen Link http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104quelle
onAttach()
?Sie müssen
instantiateItem
die Kompatibilität mit internenmakeFragmentName
Methoden nicht überschreiben oder sich darauf verlassen, dass Sie Fragment-Tags manuell erstellen.instantiateItem
ist eine öffentliche Methode, daher können und sollten Sie sie in deronCreate
Methode Ihrer Aktivität aufrufen , die von AufrufenstartUpdate
undfinishUpdate
Methoden umgeben ist, wie inPagerAdapter
javadoc beschrieben :Auf diese Weise können Sie bei Bedarf Verweise auf Instanzen Ihrer Fragmente auf lokalen Variablen speichern. Siehe Beispiel:
instantiateItem
Ich werde zuerst versuchen, Verweise auf vorhandene Fragmentinstanzen von abzurufenFragmentManager
. Nur wenn sie noch nicht existieren, werden neue mit erstelltgetItem
Methode Ihres Adapters undFragmentManager
für die zukünftige Verwendung in der "gespeichert" .Es ist wichtig zu beachten, dass Sie auch dann, wenn Sie keine Verweise auf Ihre Fragmente benötigen,
instantiateItem
alle vonstartUpdate
/finishUpdate
in IhreronCreate
Methode umgebenen Registerkarten wie folgt aufrufen sollten :Wenn Sie dies nicht tun, dann riskieren Sie , dass Ihre Fragment Instanzen werden niemals begangen zu
FragmentManager
: wenn Ihre Aktivität wird VordergrundinstantiateItem
automatisch Ihre Fragmente aufgerufen werden , um zu erhalten, aberstartUpdate
/finishUpdate
kann nicht (je nach Implementierungsdetails) und was sie im Grunde do is begin / commit aFragmentTransaction
.Dies kann dazu führen, dass Verweise auf die erstellten Fragmentinstanzen sehr schnell verloren gehen (z. B. wenn Sie Ihren Bildschirm drehen) und viel häufiger als erforderlich neu erstellt werden. Je nachdem, wie "schwer" Ihre Fragmente sind, kann dies nicht zu vernachlässigende Auswirkungen auf die Leistung haben.
Darüber hinaus können in solchen Fällen Fälle von Fragmenten, die auf lokalen Variablen gespeichert sind, auftretenveraltet werden: Wenn die Android-Plattform versucht, sie aus zu erhalten,
FragmentManager
Aus irgendeinem Grund wird es fehlschlagen und somit neue erstellen und verwenden, während Ihre Vars weiterhin auf die alten verweisen.quelle
FragmentManager
kann deine nicht einfach zufällig töten ( zerstören ist hier das richtige Wort)Fragment
(denke darüber nach, was passieren würde, wenn es sich entscheiden würde, eine zu tötenFragment
, die gerade angezeigt wird;)). Im Allgemeinen ist der Lebenszyklus von aFragment
an seinen gebundenActivity
(siehe github.com/xxv/android-lifecycle für Details) -> aFragment
kann nur zerstört werden, wenn seinActivity
zerstört wurde. In einem solchen Fall , wenn hinten ein Benutzer navigiert zu dem gegebenenActivity
seineonCreate
wieder aufgerufen werden und eine neue Instanz derFragment
erstellt wird.So wie ich es gemacht habe, definiere ich eine Hashtabelle mit schwachen Referenzen wie folgt:
Dann habe ich die Methode getItem () folgendermaßen geschrieben:
Dann können Sie eine Methode schreiben:
Das scheint gut zu funktionieren und ich finde es etwas weniger hackig als das
Trick, da es nicht darauf ankommt, wie der FragmentPagerAdapter implementiert ist. Wenn das Fragment vom FragmentPagerAdapter freigegeben wurde oder noch nicht erstellt wurde, gibt getFragment natürlich null zurück.
Wenn jemand etwas falsch mit diesem Ansatz findet, sind Kommentare mehr als willkommen.
quelle
int fragmentId
sollte umbenannt werden inint position
Ich habe diese Methode erstellt, die für mich funktioniert, um einen Verweis auf das aktuelle Fragment zu erhalten.
quelle
Die von @ personne3000 vorgeschlagene Lösung ist nett, hat aber ein Problem: Wenn die Aktivität in den Hintergrund tritt und vom System beendet wird (um freien Speicher zu erhalten) und dann wiederhergestellt wird, ist die Lösung
fragmentReferences
leer, weilgetItem
dies nicht der Fall ist namens.Die folgende Klasse behandelt eine solche Situation:
quelle
Das Hauptproblem beim Erhalten eines Handles für die Fragmente ist, dass Sie sich nicht auf getItem () verlassen können. Nach einer Orientierungsänderung sind Verweise auf die Fragmente null und getItem () wird nicht erneut aufgerufen.
Hier ist ein Ansatz, der nicht auf der Implementierung von FragmentPagerAdapter beruht, um das Tag abzurufen. Überschreiben Sie instantiateItem (), um das aus getItem () erstellte oder vom Fragmentmanager gefundene Fragment zurückzugeben.
quelle
Weitere Informationen zum Zurückgeben von Fragmenten aus dem FragmentPagerAdapter finden Sie in diesem Beitrag . Verlässt sich darauf, dass Sie den Index Ihres Fragments kennen - dies wird jedoch in getItem () festgelegt (nur bei Instanziierung).
quelle
Ich habe es geschafft, dieses Problem zu lösen, indem ich IDs anstelle von Tags verwendet habe. (Ich verwende FragmentStatePagerAdapter definiert, der meine benutzerdefinierten Fragmente verwendet, in denen ich die onAttach-Methode überschrieben habe, in der Sie die ID irgendwo speichern:
Und dann greifen Sie einfach innerhalb der Aktivität einfach auf das Fragment zu:
quelle
Ich weiß nicht, ob dies der beste Ansatz ist, aber nichts anderes hat für mich funktioniert. Alle anderen Optionen, einschließlich getActiveFragment, gaben null zurück oder führten zum Absturz der App.
Ich habe festgestellt, dass bei der Bildschirmdrehung das Fragment angehängt wurde, sodass ich es verwendet habe, um das Fragment an die Aktivität zurückzusenden.
Im Fragment:
Dann in der Aktivität:
Und schließlich in Aktivität onCreate ():
Bei diesem Ansatz wird das tatsächlich sichtbare Fragment an die Aktivität angehängt, ohne dass ein neues erstellt wird.
quelle
Ich bin mir nicht sicher, ob meine Methode der richtige oder beste Weg war, da ich ein relativer Anfänger mit Java / Android bin, aber sie hat funktioniert (ich bin sicher, dass sie gegen objektorientierte Prinzipien verstößt, aber für meinen Anwendungsfall hat keine andere Lösung funktioniert).
Ich hatte eine Hosting-Aktivität, die einen ViewPager mit einem FragmentStatePagerAdapter verwendete. Um Verweise auf die Fragmente zu erhalten, die von FragmentStatePagerAdapter erstellt wurden, habe ich eine Rückrufschnittstelle innerhalb der Fragmentklasse erstellt:
In der Hosting-Aktivität habe ich die Schnittstelle implementiert und ein LinkedHasSet erstellt, um die Fragmente zu verfolgen:
Innerhalb der ViewPagerFragment-Klasse habe ich die Fragmente der Liste in onAttach hinzugefügt und sie in onDetach entfernt:
Innerhalb der Hosting-Aktivität können Sie jetzt mFragments verwenden, um die Fragmente zu durchlaufen, die derzeit im FragmentStatePagerAdapter vorhanden sind.
quelle
Diese Klasse macht den Trick, ohne sich auf interne Tags zu verlassen. Warnung: Auf Fragmente sollte mit der Methode getFragment und nicht mit der Methode getItem zugegriffen werden.
quelle
Probieren Sie einfach diesen Code aus.
quelle