Hier ist das Szenario: Aktivität enthält Fragment A
, das wiederum Verwendungen getChildFragmentManager()
Fragmente hinzuzufügen A1
und A2
in seinem onCreate
etwa so:
getChildFragmentManager()
.beginTransaction()
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
So weit, so gut, alles läuft wie erwartet.
Wir führen dann die folgende Transaktion in der Aktivität aus:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(anim1, anim2, anim1, anim2)
.replace(R.id.fragmentHolder, new FragmentB())
.addToBackStack(null)
.commit()
Während des Übergangs werden die enter
Animationen für das Fragment B
korrekt ausgeführt, die Fragmente A1 und A2 verschwinden jedoch vollständig . Wenn wir die Transaktion mit der Schaltfläche Zurück zurücksetzen, werden sie ordnungsgemäß initialisiert und während der popEnter
Animation normal angezeigt .
In meinen kurzen Tests wurde es seltsamer - wenn ich die Animationen für die untergeordneten Fragmente einstelle (siehe unten), wird die exit
Animation zeitweise ausgeführt, wenn wir Fragmente hinzufügenB
getChildFragmentManager()
.beginTransaction()
.setCustomAnimations(enter, exit)
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
Der Effekt, den ich erzielen möchte, ist einfach: Ich möchte, dass die Animation exit
(oder sollte es sein popExit
?) Auf Fragment A
(anim2) ausgeführt wird und den gesamten Container einschließlich seiner verschachtelten untergeordneten Elemente animiert.
Gibt es eine Möglichkeit, dies zu erreichen?
Bearbeiten : Hier finden Sie einen Testfall hier
Edit2 : Vielen Dank an @StevenByle, der mich dazu gedrängt hat, es weiter mit den statischen Animationen zu versuchen. Anscheinend können Sie Animationen pro Operation festlegen (nicht global für die gesamte Transaktion), was bedeutet, dass die Kinder einen unbestimmten statischen Animationssatz haben können, während ihre Eltern eine andere Animation haben können und das Ganze in einer Transaktion festgeschrieben werden kann . Siehe die folgende Diskussion und das aktualisierte Testfallprojekt .
R.id.fragmentHolder
in Bezug auf A, A1, A2 usw.?changeFragment
Methode nur einmal aufrufen ?Antworten:
Um zu vermeiden, dass der Benutzer sieht, dass die verschachtelten Fragmente verschwinden, wenn das übergeordnete Fragment in einer Transaktion entfernt / ersetzt wird, können Sie die noch vorhandenen Fragmente "simulieren", indem Sie ein Bild von ihnen bereitstellen, wie sie auf dem Bildschirm angezeigt werden. Dieses Bild wird als Hintergrund für den Container für verschachtelte Fragmente verwendet. Selbst wenn die Ansichten des verschachtelten Fragments verschwinden, simuliert das Bild deren Vorhandensein. Außerdem sehe ich es nicht als Problem an, die Interaktivität mit den Ansichten des verschachtelten Fragments zu verlieren, da ich nicht glaube, dass der Benutzer auf sie reagieren soll, wenn sie gerade entfernt werden (wahrscheinlich als Benutzeraktion als Gut).
Ich habe ein kleines Beispiel mit dem Einrichten des Hintergrundbilds gemacht (etwas Grundlegendes).
quelle
Es scheint also viele verschiedene Problemumgehungen zu geben, aber basierend auf der Antwort von @ Jayd16 denke ich, dass ich eine ziemlich solide Gesamtlösung gefunden habe, die immer noch benutzerdefinierte Übergangsanimationen für untergeordnete Fragmente ermöglicht und dies nicht erfordert ein Bitmap-Cache des Layouts.
Haben Sie eine
BaseFragment
Klasse, die erweitert wirdFragment
, und lassen Sie alle Ihre Fragmente diese Klasse erweitern (nicht nur untergeordnete Fragmente).Fügen Sie in dieser
BaseFragment
Klasse Folgendes hinzu:Es erfordert leider Reflexion; Da diese Problemumgehung jedoch für die Support-Bibliothek gilt, besteht nicht das Risiko, dass sich die zugrunde liegende Implementierung ändert, es sei denn, Sie aktualisieren Ihre Support-Bibliothek. Wenn Sie die Support-Bibliothek aus dem Quellcode erstellen, können Sie einen Accessor für die nächste Animationsressourcen-ID hinzufügen
Fragment.java
und die Notwendigkeit der Reflexion beseitigen.Diese Lösung beseitigt die Notwendigkeit, die Animationsdauer des Elternteils zu "erraten" (so dass die Animation "Nichts tun" dieselbe Dauer hat wie die Exit-Animation des Elternteils), und ermöglicht es Ihnen, weiterhin benutzerdefinierte Animationen für untergeordnete Fragmente zu erstellen (z. Kinderfragmente mit verschiedenen Animationen austauschen).
quelle
mNextAnim
ist jetzt in einemmAnimationInfo
Objekt. Sie können wieField animInfoField = Fragment.class.getDeclaredField("mAnimationInfo");
animInfoField.setAccessible(true);
Object animationInfo = animInfoField.get(fragment);
Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
val nextAnimResource = nextAnimField.getInt(animationInfo);
um die Linie zu ersetzenint nextAnimResource = nextAnimField.getInt(fragment);
Ich konnte eine ziemlich saubere Lösung finden. IMO ist das am wenigsten hackige, und obwohl dies technisch die "Draw a Bitmap" -Lösung ist, wird es zumindest durch das Fragment lib abstrahiert.
Stellen Sie sicher, dass Ihre Kinder-Frags eine Elternklasse folgendermaßen überschreiben:
Wenn die untergeordneten Frags eine Exit-Animation haben, werden sie animiert, anstatt wegzublinken. Wir können dies ausnutzen, indem wir eine Animation haben, die einfach die untergeordneten Fragmente für eine Dauer mit vollem Alpha zeichnet. Auf diese Weise bleiben sie während der Animation im übergeordneten Fragment sichtbar und geben das gewünschte Verhalten an.
Das einzige Problem, an das ich denken kann, ist, diese Dauer im Auge zu behalten. Ich könnte es vielleicht auf eine große Zahl einstellen, aber ich befürchte, dass es Leistungsprobleme geben könnte, wenn diese Animation noch irgendwo gezeichnet wird.
quelle
Ich poste meine Lösung aus Gründen der Klarheit. Die Lösung ist ganz einfach. Wenn Sie versuchen, die Fragmenttransaktionsanimation des übergeordneten Elements nachzuahmen, fügen Sie der untergeordneten Fragmenttransaktion einfach eine benutzerdefinierte Animation mit derselben Dauer hinzu. Oh und stellen Sie sicher, dass Sie die benutzerdefinierte Animation festlegen, bevor Sie add ().
Die XML-Datei für R.anim.none (Die Animationszeit meiner Eltern für das Ein- und Aussteigen beträgt 250 ms)
quelle
fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
Ich verstehe, dass dies möglicherweise nicht in der Lage ist, Ihr Problem vollständig zu lösen, aber vielleicht wird es den Bedürfnissen eines anderen entsprechen. Sie können Ihren Kindern
enter
/exit
undpopEnter
/popExit
Animationen hinzufügen , die dieFragment
s nicht wirklich bewegen / animierenFragment
. Solange die Animationen dieselbe Dauer / denselben Versatz wie ihre übergeordnetenFragment
Animationen haben, scheinen sie sich mit der Animation der übergeordneten Animation zu bewegen / zu animieren.quelle
Sie können dies im untergeordneten Fragment tun.
quelle
@@@@@@@@@@@@@@@@@@@@@@@@@@@
EDIT: Am Ende habe ich diese Lösung nicht implementiert, da es andere Probleme gab, die dies hat. Square hat kürzlich zwei Bibliotheken herausgebracht, die Fragmente ersetzen. Ich würde sagen, dies könnte tatsächlich eine bessere Alternative sein, als zu versuchen, Fragmente in etwas zu hacken, das Google nicht will.
http://corner.squareup.com/2014/01/mortar-and-flow.html
@@@@@@@@@@@@@@@@@@@@@@@@@@@
Ich dachte, ich würde diese Lösung aufstellen, um Menschen zu helfen, die dieses Problem in Zukunft haben. Wenn Sie die Konversation der Originalposter mit anderen Personen nachverfolgen und sich den von ihm geposteten Code ansehen, werden Sie feststellen, dass das Originalposter schließlich zu dem Schluss kommt, dass beim Animieren des übergeordneten Fragments eine No-Op-Animation für die untergeordneten Fragmente verwendet wird. Diese Lösung ist nicht ideal, da Sie gezwungen sind, alle untergeordneten Fragmente im Auge zu behalten. Dies kann bei Verwendung eines ViewPagers mit FragmentPagerAdapter umständlich sein.
Da ich überall Kinderfragmente verwende, habe ich diese Lösung entwickelt, die effizient und modular ist (so dass sie leicht entfernt werden kann), falls sie jemals repariert wird und diese No-Op-Animation nicht mehr benötigt wird.
Es gibt viele Möglichkeiten, wie Sie dies implementieren können. Ich habe mich für einen Singleton entschieden und nenne ihn ChildFragmentAnimationManager. Grundsätzlich wird ein untergeordnetes Fragment für mich anhand seines übergeordneten Elements verfolgt und auf Anfrage eine No-Op-Animation auf die Kinder angewendet.
Als nächstes benötigen Sie eine Klasse, die Fragment erweitert, die alle Ihre Fragmente erweitern (zumindest Ihre untergeordneten Fragmente). Ich hatte diese Klasse bereits und nenne sie BaseFragment. Wenn eine Fragmentansicht erstellt wird, fügen wir sie dem ChildFragmentAnimationManager hinzu und entfernen sie, wenn sie zerstört wird. Sie können dies mit Attach / Detach oder anderen Übereinstimmungsmethoden in der Sequenz tun. Meine Logik für die Auswahl "Ansicht erstellen / zerstören" war, dass es mir egal ist, ein Fragment zu animieren, damit es weiterhin angezeigt wird, wenn es keine Ansicht hat. Dieser Ansatz sollte auch mit ViewPagern besser funktionieren, die Fragmente verwenden, da Sie nicht jedes einzelne Fragment verfolgen, das ein FragmentPagerAdapter enthält, sondern nur 3.
Nachdem alle Ihre Fragmente vom übergeordneten Fragment gespeichert wurden, können Sie sie wie folgt animieren, und Ihre untergeordneten Fragmente verschwinden nicht mehr.
Nur damit Sie es haben, finden Sie hier die Datei no_anim.xml, die sich in Ihrem Ordner res / anim befindet:
Auch hier denke ich nicht, dass diese Lösung perfekt ist, aber sie ist viel besser als für jede Instanz, in der Sie ein untergeordnetes Fragment haben, und implementiert benutzerdefinierten Code im übergeordneten Fragment, um die Übersicht über jedes untergeordnete Fragment zu behalten. Ich war dort und es macht keinen Spaß.
quelle
Ich glaube, ich habe eine bessere Lösung für dieses Problem gefunden, als das aktuelle Fragment in einer Bitmap zu fotografieren, wie Luksprog vorgeschlagen hat.
Der Trick besteht darin , das entfernte oder abgetrennte Fragment auszublenden. Erst nach Abschluss der Animationen wird das Fragment in einer eigenen Fragmenttransaktion entfernt oder abgetrennt.
Stellen Sie sich vor, wir haben
FragmentA
undFragmentB
beide mit Unterfragmenten. Nun, wenn Sie normalerweise tun würden:Stattdessen tust du es
Nun zur Implementierung des Fragments:
quelle
Ich hatte das gleiche Problem mit dem Kartenfragment. Es verschwand während der Exit-Animation seines enthaltenen Fragments. Die Problemumgehung besteht darin, eine Animation für das untergeordnete Kartenfragment hinzuzufügen, die während der Exit-Animation des übergeordneten Fragmentes sichtbar bleibt. Die Animation des untergeordneten Fragments hält sein Alpha während seiner Dauer bei 100%.
Animation: res / animator / keep_child_fragment.xml
Die Animation wird dann angewendet, wenn das Kartenfragment dem übergeordneten Fragment hinzugefügt wird.
Übergeordnetes Fragment
Schließlich wird die Dauer der untergeordneten Fragmentanimation in einer Ressourcendatei festgelegt.
values / integers.xml
quelle
Um das Verschwinden verschachtelter Fragmente zu animieren, können wir ChildFragmentManager einen Pop-Back-Stapel erzwingen. Dadurch wird eine Übergangsanimation ausgelöst. Dazu müssen wir das OnBackButtonPressed-Ereignis nachholen oder auf Backstack-Änderungen warten.
Hier ist ein Beispiel mit Code.
quelle
Ich bin kürzlich in meiner Frage auf dieses Problem gestoßen : Verschachtelte Fragmente, die falsch übergehen
Ich habe eine Lösung, die dies löst, ohne eine Bitmap zu speichern oder Reflexionen oder andere unbefriedigende Methoden zu verwenden.
Ein Beispielprojekt kann hier angesehen werden: https://github.com/zafrani/NestedFragmentTransitions
Ein GIF des Effekts kann hier eingesehen werden: https://imgur.com/94AvrW4
In meinem Beispiel gibt es 6 untergeordnete Fragmente, die auf zwei übergeordnete Fragmente aufgeteilt sind. Ich kann die Übergänge für Enter, Exit, Pop und Push problemlos erreichen. Konfigurationsänderungen und Zurückdrücken werden ebenfalls erfolgreich behandelt.
Der Großteil der Lösung befindet sich in der onCreateAnimator-Funktion von BaseFragment (dem von meinen untergeordneten und übergeordneten Fragmenten erweiterten Fragment), die folgendermaßen aussieht:
Die Aktivität und das übergeordnete Fragment sind für das Festlegen der Zustände dieser Booleschen Werte verantwortlich. Es ist einfacher zu sehen, wie und wo von meinem Beispielprojekt.
In meinem Beispiel werden keine Unterstützungsfragmente verwendet, aber mit ihnen und ihrer onCreateAnimation-Funktion kann dieselbe Logik verwendet werden
quelle
Eine einfache Möglichkeit, dieses Problem zu beheben, besteht darin, die
Fragment
Klasse aus dieser Bibliothek anstelle der Standardbibliotheksfragmentklasse zu verwenden:https://github.com/marksalpeter/contract-fragment
Nebenbei bemerkt enthält das Paket auch ein nützliches Delegatenmuster,
ContractFragment
das Sie möglicherweise zum Erstellen Ihrer Apps unter Nutzung der Eltern-Kind-Fragmentbeziehung als nützlich erachten.quelle
Aus der obigen Antwort von @kcoppock,
Wenn Sie Aktivität-> Fragment-> Fragmente haben (Mehrfachstapelung, das Folgende hilft), eine kleinere Bearbeitung, um meiner Meinung nach die beste Antwort zu erhalten.
quelle
Mein Problem war das Entfernen des übergeordneten Fragments (ft.remove (Fragment)), untergeordnete Animationen wurden nicht ausgeführt.
Das Grundproblem besteht darin, dass untergeordnete Fragmente sofort VOR dem Verlassen der Animation durch das übergeordnete Fragment ZERSTÖRT werden.
Benutzerdefinierte Animationen für untergeordnete Fragmente werden beim Entfernen des übergeordneten Fragments nicht ausgeführt
Wie andere sich entzogen haben, ist es der richtige Weg, die Eltern (und nicht das Kind) vor der Entfernung der Eltern zu verstecken.
Wenn Sie das übergeordnete Element tatsächlich entfernen möchten, sollten Sie wahrscheinlich einen Listener für Ihre benutzerdefinierte Animation einrichten, um zu wissen, wann die Animation beendet ist, damit Sie das übergeordnete Fragment sicher entfernen können (entfernen). Wenn Sie dies nicht rechtzeitig tun, können Sie die Animation möglicherweise beenden. Hinweis: Die Animation wird in einer eigenen asynchronen Warteschlange ausgeführt.
Übrigens benötigen Sie keine benutzerdefinierten Animationen für das untergeordnete Fragment, da diese die übergeordneten Animationen erben.
quelle
Das Problem wurde in behoben
androidx.fragment:fragment:1.2.0-alpha02
. Weitere Informationen finden Sie unter https://issuetracker.google.com/issues/116675313 .quelle
Alter Thread, aber für den Fall, dass hier jemand stolpert:
Alle oben genannten Ansätze fühlen sich für mich sehr unattraktiv an. Die Bitmap-Lösung ist sehr schmutzig und nicht performant. Bei den anderen müssen die untergeordneten Fragmente die Dauer des Übergangs kennen, der in der Transaktion zum Erstellen des betreffenden untergeordneten Fragments verwendet wird. Eine bessere Lösung in meinen Augen ist so etwas wie die folgende:
Wir verstecken nur das aktuelle Fragment und fügen das neue Fragment hinzu. Wenn die Animation beendet ist, entfernen wir das alte Fragment. Auf diese Weise wird es an einem Ort behandelt und es wird keine Bitmap erstellt.
quelle