TL; DR: Ich suche nach einem vollständigen Beispiel für das Szenario "Google Mail-Drei-Fragment-Animation". Insbesondere möchten wir mit zwei Fragmenten beginnen:
Bei einem UI-Ereignis (z. B. Tippen auf etwas in Fragment B) möchten wir:
- Fragment A, um vom Bildschirm nach links zu rutschen
- Fragment B wird an den linken Bildschirmrand geschoben und verkleinert, um die von Fragment A frei gewordene Stelle einzunehmen
- Fragment C, um von der rechten Seite des Bildschirms hinein zu gleiten und die von Fragment B frei gewordene Stelle einzunehmen
Und bei einem BACK-Tastendruck möchten wir, dass diese Operationen umgekehrt werden.
Jetzt habe ich viele Teilimplementierungen gesehen; Ich werde vier davon unten überprüfen. Abgesehen davon, dass sie unvollständig sind, haben sie alle ihre Probleme.
@Reto Meier hat diese beliebte Antwort auf dieselbe grundlegende Frage beigesteuert und darauf hingewiesen , dass Sie sie setCustomAnimations()
mit a verwenden würden FragmentTransaction
. Für ein Szenario mit zwei Fragmenten (z. B. sehen Sie Fragment A zunächst nur und möchten es mithilfe animierter Effekte durch ein neues Fragment B ersetzen) stimme ich voll und ganz zu. Jedoch:
- Da Sie nur eine "In" - und eine "Out" -Animation angeben können, kann ich nicht sehen, wie Sie mit all den verschiedenen Animationen umgehen würden, die für das Drei-Fragment-Szenario erforderlich sind
- Der
<objectAnimator>
in seinem Beispielcode verwendete fest verdrahtete Positionen in Pixel, und dies scheint angesichts unterschiedlicher Bildschirmgrößen unpraktisch zu sein,setCustomAnimations()
erfordert jedoch Animationsressourcen, was die Möglichkeit ausschließt, diese Dinge in Java zu definieren - Ich bin ratlos , wie das Objekt Animateure für Skala Krawatte in mit Dingen wie
android:layout_weight
in einemLinearLayout
Raum auf prozentuale Basis für die Zuteilung - Ich weiß nicht, wie Fragment C zu Beginn behandelt wird (
GONE
?android:layout_weight
Von0
? Voranimiert auf eine Skala von 0? Etwas anderes?)
@Roman Nurik weist darauf hin, dass Sie jede Eigenschaft animieren können , auch solche, die Sie selbst definieren. Dies kann helfen, das Problem der fest verdrahteten Positionen zu lösen, und zwar auf Kosten der Erfindung Ihrer eigenen benutzerdefinierten Layout-Manager-Unterklasse. Das hilft einigen, aber ich bin immer noch verblüfft über den Rest der Reto-Lösung.
Der Autor dieses Pastebin-Eintrags zeigt einen verlockenden Pseudocode, der im Grunde besagt, dass sich alle drei Fragmente anfänglich im Container befinden würden, wobei Fragment C zu Beginn über eine hide()
Transaktionsoperation ausgeblendet wurde . Wir dann show()
C und hide()
A, wenn das UI-Ereignis auftritt. Ich sehe jedoch nicht, wie das mit der Tatsache umgeht, dass B die Größe ändert. Es hängt auch von der Tatsache ab, dass Sie anscheinend mehrere Fragmente zum selben Container hinzufügen können, und ich bin mir nicht sicher, ob dies auf lange Sicht ein verlässliches Verhalten ist oder nicht (ganz zu schweigen davon findFragmentById()
, dass es kaputt gehen sollte , obwohl ich damit leben kann).
Der Autor dieses Blogposts gibt an, dass Google Mail überhaupt keine verwendet setCustomAnimations()
, sondern direkt Objektanimatoren verwendet ("Sie ändern nur den linken Rand der Stammansicht + ändern die Breite der rechten Ansicht"). Dies ist jedoch immer noch eine AFAICT-Lösung mit zwei Fragmenten, und die Implementierung zeigt erneut die Abmessungen der Festdrähte in Pixel.
Ich werde mich weiterhin damit beschäftigen, damit ich eines Tages selbst darauf antworten kann, aber ich hoffe wirklich, dass jemand die Drei-Fragment-Lösung für dieses Animationsszenario ausgearbeitet hat und den Code (oder einen Link dazu) veröffentlichen kann. Animationen in Android bringen mich dazu, mir die Haare auszureißen, und diejenigen unter Ihnen, die mich gesehen haben, wissen, dass dies ein weitgehend erfolgloses Unterfangen ist.
quelle
Antworten:
Mein Vorschlag wurde bei github hochgeladen (Funktioniert mit allen Android-Versionen, obwohl die Anzeige der Hardwarebeschleunigung für diese Art von Animationen dringend empfohlen wird. Für nicht hardwarebeschleunigte Geräte sollte eine Bitmap-Caching-Implementierung besser passen.)
Das Demo-Video mit der Animation ist hier (langsame Bildrate aufgrund der Bildschirmdarstellung. Die tatsächliche Leistung ist sehr schnell).
Verwendung:
Erklärung :
Erstellt ein neues benutzerdefiniertes RelativeLayout (ThreeLayout) und 2 benutzerdefinierte Animationen (MyScalAnimation, MyTranslateAnimation)
ThreeLayout
Ruft das Gewicht des linken Bereichs als Parameter ab, vorausgesetzt, die andere sichtbare Ansicht hatweight=1
.So
new ThreeLayout(context,3)
entsteht eine neue Ansicht mit 3 Kindern und der linke Bereich hat 1/3 des Gesamtbildschirms. Die andere Ansicht belegt den gesamten verfügbaren Platz.Animationen skalieren und übersetzen ändern tatsächlich die Größe und verschieben die Ansicht und nicht pseudo- [skalieren, verschieben]. Beachten Sie, dass
fillAfter(true)
nirgendwo verwendet wird.View2 ist rechts von View1
und
View3 ist rechts von View2
Nachdem Sie diese Regeln festgelegt haben, kümmert sich RelativeLayout um alles andere. Animationen verändern die
margins
(in Bewegung) und[width,height]
im MaßstabUm auf jedes Kind zuzugreifen (damit Sie es mit Ihrem Fragment aufblasen können, können Sie es aufrufen
Unten werden die 2 Animationen gezeigt
Stufe 1
Stufe 2
quelle
View
verkleinert wird), aber in Google Mail / E-Mail erhalten wir keine kleineren Schriftarten, sondern nur weniger Wörter.scaleX
Wert aufView
, obwohl ich Dinge möglicherweise falsch interpretiere.OK, hier ist meine eigene Lösung, abgeleitet von der E-Mail-AOSP-App, gemäß dem Vorschlag von @ Christopher in den Kommentaren der Frage.
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane
Die Lösung von @ schwachwire erinnert an meine, obwohl er
Animation
eher klassische als Animatoren verwendet undRelativeLayout
Regeln verwendet, um die Positionierung durchzusetzen. Vom Standpunkt der Prämie aus wird er wahrscheinlich die Prämie erhalten, es sei denn, jemand anderes mit einer besseren Lösung gibt noch eine Antwort.Kurz gesagt, das
ThreePaneLayout
in diesem Projekt ist eineLinearLayout
Unterklasse, die entworfen wurde, um mit drei Kindern in der Landschaft zu arbeiten. Die Breiten dieser Kinder können im Layout-XML mit den gewünschten Mitteln festgelegt werden. Ich zeige die Verwendung von Gewichten, aber Sie können bestimmte Breiten durch Dimensionsressourcen oder was auch immer festlegen. Das dritte Kind - Fragment C in der Frage - sollte eine Breite von Null haben.Unabhängig davon, wie Sie die Breiten ursprünglich eingerichtet haben, wird zur Laufzeit die Breitenverwaltung übernommen, wenn Sie
ThreePaneLayout
zum ersten MalhideLeft()
von der Anzeige der als Fragmente A und B bezeichneten Fragen zu den Fragmenten B und C wechseln. In der Terminologie vonThreePaneLayout
- die keine spezifische Bindung an Fragmenten hat - die drei Stücke sindleft
,middle
undright
. Zum Zeitpunkt Ihres AnrufshideLeft()
zeichnen wir die Größenleft
undmiddle
und Null aller Gewichte auf, die für eine der drei verwendet wurden, sodass wir die Größen vollständig steuern können. Zum Zeitpunkt von setzenhideLeft()
wir die Größe vonright
auf die ursprüngliche Größe vonmiddle
.Die Animationen sind zweifach:
ViewPropertyAnimator
, umleft
mithilfe einer Hardwareebene eine Übersetzung der drei Widgets links um die Breite von durchzuführenObjectAnimator
für eine benutzerdefinierte Pseudo-Eigenschaft vonmiddleWidth
, um diemiddle
Breite von der ursprünglichen Breite auf die ursprüngliche Breite von zu ändernleft
(Es ist möglich, dass es eine bessere Idee ist, ein
AnimatorSet
undObjectAnimators
für all diese zu verwenden, obwohl dies vorerst funktioniert.)(Es ist auch möglich, dass der
middleWidth
ObjectAnimator
Wert der Hardwareschicht negiert wird, da dies eine ziemlich kontinuierliche Ungültigmachung erfordert.)(Es ist definitiv möglich, dass ich immer noch Lücken in meinem Animationsverständnis habe und dass ich Aussagen in Klammern mag)
Der Nettoeffekt besteht darin, dass
left
er vom Bildschirmmiddle
rutscht, an die ursprüngliche Position und Größe von gleitetleft
undright
direkt dahinter übersetzt wirdmiddle
.showLeft()
kehrt einfach den Prozess um, mit der gleichen Mischung von Animatoren, nur mit umgekehrten Richtungen.Die Aktivität verwendet a
ThreePaneLayout
, um ein PaarListFragment
Widgets und a zu haltenButton
. Wenn Sie im linken Fragment etwas auswählen, wird das mittlere Fragment hinzugefügt (oder dessen Inhalt aktualisiert). Wenn Sie etwas im mittleren Fragment auswählen, wird die Beschriftung desButton
Plus festgelegt undhideLeft()
auf dem ausgeführtThreePaneLayout
. Wenn Sie die linke Seite ausgeblendet haben, wird BACK ausgeführtshowLeft()
. Andernfalls beendet BACK die Aktivität. Da dies keineFragmentTransactions
Auswirkungen auf die Animationen hat, können wir diesen "Backstack" nicht selbst verwalten.Das oben verlinkte Projekt verwendet native Fragmente und das native Animator-Framework. Ich habe eine andere Version desselben Projekts, die den Backport für Android- Supportfragmente und NineOldAndroids für die Animation verwendet:
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC
Der Backport funktioniert auf einem Kindle Fire der 1. Generation einwandfrei, obwohl die Animation aufgrund der niedrigeren Hardwarespezifikationen und der fehlenden Unterstützung für die Hardwarebeschleunigung etwas ruckelig ist. Beide Implementierungen scheinen auf einem Nexus 7 und anderen Tablets der aktuellen Generation reibungslos zu sein.
Ich bin auf jeden Fall offen für Ideen, wie diese Lösung verbessert werden kann, oder für andere Lösungen, die klare Vorteile gegenüber dem bieten, was ich hier gemacht habe (oder was @weakwire verwendet hat).
Nochmals vielen Dank an alle, die dazu beigetragen haben!
quelle
ListView
und die Zurück-Taste schnell drückte und wiederholte, driftete das gesamte Layout nach rechts. Es wurde links eine zusätzliche Leerzeichenspalte angezeigt. Dieses Verhalten wurde eingeführt, weil ich die erste Animation (die durch Tippen auf die Mitte gestartet wurdeListView
) nicht abgeschlossen und die Zurück-Taste gedrückt habe. I fixierte es durch das ErsetzentranslationXBy
mittranslationX
intranslateWidgets
Verfahren undtranslateWidgets(leftWidth, left, middle, right);
mittranslateWidgets(0, left, middle, right);
in -showLeft()
Verfahren.requestLayout();
mitmiddle.requestLayout();
in -setMiddleWidth(int value);
Verfahren. Es hat möglicherweise keinen Einfluss auf die Leistung, da die GPU vermutlich immer noch einen vollständigen Layoutdurchlauf durchführt.Wir haben eine Bibliothek namens PanesLibrary erstellt, die dieses Problem löst. Es ist noch flexibler als das, was bisher angeboten wurde, weil:
Sie können es hier überprüfen: https://github.com/Mapsaurus/Android-PanesLibrary
Hier ist eine Demo: http://www.youtube.com/watch?v=UA-lAGVXoLU&feature=youtu.be
Grundsätzlich können Sie auf einfache Weise eine beliebige Anzahl von Fenstern mit dynamischer Größe hinzufügen und Fragmente an diese Fenster anhängen. Ich hoffe, Sie finden es nützlich! :) :)
quelle
Aufbauend auf einem der Beispiele, auf die Sie verlinkt haben ( http://android.amberfog.com/?p=758 ), wie wäre es mit einer Animation der
layout_weight
Eigenschaft? Auf diese Weise können Sie die Gewichtsänderung der 3 Fragmente zusammen animieren UND erhalten den Bonus, dass sie alle gut zusammengleiten:Beginnen Sie mit einem einfachen Layout. Da wir animieren werden
layout_weight
, benötigen wir eineLinearLayout
als Root-Ansicht für die 3 Panels:Dann die Demo-Klasse:
Auch in diesem Beispiel werden FragmentTransactions nicht verwendet, um die Animationen zu erstellen (vielmehr werden die Ansichten animiert, an die die Fragmente angehängt sind). Sie müssten also alle Backstack- / Fragmenttransaktionen selbst ausführen, aber im Vergleich zum Aufwand, die Animationen zum Laufen zu bringen schön, das scheint kein schlechter Kompromiss zu sein :)
Schreckliches Video mit niedriger Auflösung in Aktion: http://youtu.be/Zm517j3bFCo
quelle
TextView
mitwrap_content
sich ausstrecken vertikal wie die Animation Erlös). Es kann jedoch sein, dass beim Animieren des Gewichts die Größe geändert wird, die ich als Fragment B bezeichnet habe. Danke!Hier werden keine Fragmente verwendet. Es handelt sich um ein benutzerdefiniertes Layout mit 3 untergeordneten Elementen. Wenn Sie auf eine Nachricht klicken, versetzen Sie die 3 Kinder mit
offsetLeftAndRight()
und einem Animator.In können
JellyBean
Sie "Layoutgrenzen anzeigen" in den Einstellungen "Entwickleroptionen" aktivieren. Wenn die Folienanimation abgeschlossen ist, können Sie immer noch sehen, dass das linke Menü noch vorhanden ist, jedoch unter dem mittleren Bereich.Es ähnelt dem Fly-In-App-Menü von Cyril Mottier , enthält jedoch 3 statt 2 Elemente.
Darüber hinaus ist das
ViewPager
dritte Kind ein weiterer Hinweis auf dieses Verhalten:ViewPager
Verwendet normalerweise Fragmente (ich weiß, dass dies nicht erforderlich ist, aber ich habe noch nie eine andere Implementierung gesehenFragment
), und da Sie die drei Kinder nichtFragments
in einem anderen verwenden könnenFragment
sind wohl keine fragmente ....quelle
TranslateAnimation
, obwohl ich es bin Sicher gibt es Möglichkeiten, Animatoren zu verwenden, um die gleichen Ziele zu erreichen. Ich muss einige Experimente dazu durchführen. Vielen Dank!Ich versuche gerade, so etwas zu tun, außer dass die Fragment B-Skala den verfügbaren Platz beansprucht und dass der 3-Bereich gleichzeitig geöffnet werden kann, wenn genügend Platz vorhanden ist. Hier ist meine bisherige Lösung, aber ich bin mir nicht sicher, ob ich dabei bleiben werde. Ich hoffe, jemand wird eine Antwort geben, die den richtigen Weg zeigt.
Anstatt ein LinearLayout zu verwenden und das Gewicht zu animieren, verwende ich ein RelativeLayout und animiere die Ränder. Ich bin mir nicht sicher, ob dies der beste Weg ist, da bei jedem Update ein Aufruf von requestLayout () erforderlich ist. Es ist jedoch auf allen meinen Geräten reibungslos.
Also, ich animiere das Layout, ich verwende keine Fragmenttransaktion. Ich gehe manuell auf die Zurück-Schaltfläche, um Fragment C zu schließen, wenn es geöffnet ist.
FragmentB verwendet layout_toLeftOf / ToRightOf, um es an Fragment A und C auszurichten.
Wenn meine App ein Ereignis auslöst, um Fragment C anzuzeigen, schiebe ich Fragment C ein und schiebe Fragment A gleichzeitig heraus. (2 separate Animation). Umgekehrt, wenn Fragment A geöffnet wird, schließe ich C gleichzeitig.
Im Hochformat oder auf einem kleineren Bildschirm verwende ich ein etwas anderes Layout und schiebe Fragment C über den Bildschirm.
Um den Prozentsatz für die Breite von Fragment A und C zu verwenden, müssten Sie ihn zur Laufzeit berechnen ... (?)
Hier ist das Layout der Aktivität:
Die Animation zum Ein- oder Ausschieben von FragmentC:
quelle