Versteckte Fehler bei Shared Element Transitions



Es gibt eine gute Vorgehensweise, um Ihre Anwendung schön und lebendig zu gestalten, und heutzutage gibt es viele Tools und Möglichkeiten, um dies zu erreichen. Einer davon ist Shared Element Transition.

In diesem Artikel werde ich einige Fehler behandeln, die mich viel Zeit gekostet haben. Ich werde zeigen, wie Sie sie vermeiden können, wenn Sie diese Art von Übergängen mit Fragmenten auf Anwendung implementieren.

Fangen Sie an


Bevor ich die Animation gemacht habe, habe ich Dutzende von Artikeln gelesen, aber die meisten befassten sich mit Activity Transition. Ich bin jedoch auf wirklich gute Informationen zu Fragmenten gestoßen, und ich möchte einen kleinen Rückblick darauf geben, wie der Übergang für gemeinsame Elemente erstellt wird.

Hier sind die wichtigsten Schritte zum Erstellen einer Animation:

  1. setReorderingAllowed(true) . Sie können einen Aufruf der Lebenszyklusmethoden neu anordnen und Ihre Animation wird korrekt angezeigt.
  2. Rufen Sie addSharedElement() und fügen Sie die Ansichten hinzu, die von den Bildschirmen gemeinsam genutzt werden
  3. Fügen Sie für jede Ansicht im Übergang einen eindeutigen android:transitionName TransitionName hinzu
  4. Fügen Sie sharedElementEnterTransition/sharedElementReturnTransition . Optional: Für eine bessere Wirkung können wir auch enterTransition/exitTransition .
  5. Fügen Sie postponeEnterTransition/startPostponedEnterTransition , um den Moment zu definieren, in dem die Daten geladen werden und die Benutzeroberfläche zum postponeEnterTransition/startPostponedEnterTransition bereit ist

Das scheint genug zu sein, um Animationen zu erstellen und Ihre Designer und Benutzer glücklich zu machen. ABER es gibt immer einige Unfälle. Lassen Sie uns einen Blick darauf werfen, was wir haben, wenn wir die oben aufgeführten Schritte ausführen:

Unterbrochene Animation


Das haben wir nicht erwartet. Lass es uns herausfinden.

Fehler Nr. 1. Statische Übergangsnamen (ein halber Tag verschwendet)


Wie ich bereits sagte, sollten unsere Ansichten eindeutige Übergangsnamen haben. Andernfalls kann das Übergangsframework nicht erkennen, welche Ansicht am Übergang teilnimmt, und schafft es ohne diese. Wo liegt also ein Problem?

Die Sache ist RecyclerView. Das war's.

Wenn RecyclerView vorhanden ist und eine animierende Ansicht Teil des RecyclerView-Elements ist, sollten Sie transitionName dynamisch erstellen und festlegen (XML vergessen). Außerdem sollten Sie dies in beiden Fragmenten tun, auch wenn das zweite nicht über RecyclerView verfügt.

Das Update lautet also:

 val transitionNameImage = context.getString(R.string.transition_image, title) 

Sie haben vielleicht bemerkt, dass ich "title" als Argument angegeben habe, um einen eindeutigen Namen zu erhalten. Es ist besser, das Domänenmodell anstelle beispielsweise der Elementposition auf dem Bildschirm zu verwenden, da diese Namen nicht als Argumente an Bundle übergeben und aus dem zweiten Fragment analysiert werden müssen.

Fehler Nr. 2. Ohne Berücksichtigung des Elternfragments (1,5 Tage verschwendet)


Ich weiß, Sie könnten fragen: "Wie kommt es?" Aber als ich die Artikel über gemeinsame Animationen las, betrachtete niemand ein Beispiel in der Hierarchie komplexer Fragmente. Das ist der Grund, warum Sie manchmal nicht darauf achten.

Mein erstes Fragment war also ein Kind des Fragmentcontainers und kein Wunder, dass postponeEnterTransition()/startPostponedEnterTransition() keine Wirkung hatte.

Hier ist es
 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) parentFragment?.also { parentFragment -> NewsTransitioner.setupFirstFragment(parentFragment) parentFragment.postponeEnterTransition() } // Initialization UI } 


Hier müssen Sie diese Methoden aus dem übergeordneten Fragment aufrufen.

Fehler Nr. 3. Gleiten unterschätzen (2 Tage verschwendet)


"Ok, ich habe gelernt, wie man einen gemeinsamen Übergang durchführt, wann die erforderlichen Methoden bezüglich des Lebenszyklus und des Ladens aufgerufen werden müssen. Diesmal wird es funktionieren! "

Nun, ich habe mich geirrt. Dies ist vielleicht der schwierigste Fehler, dem ich begegnet bin. Werfen wir einen Blick auf das, was wir bisher haben:

Aktuelle Animation


Möglicherweise stellen Sie fest, dass beim Eingeben des Übergangs ein seltsamer Fehler auftritt. Wenn es startet, hat das Bild bereits die Matrix geändert und bewegt sich dann einfach an die endgültige Position.

Ich möchte hier nicht die gesamte Untersuchung beschreiben. Kurz gesagt, ich hatte das Glück, über einen schönen Artikel zu stolpern.

Wo ich Lösung gefunden habe. Hier ist es:
„Wir haben diesen Fehler, weil Glide versucht, das Laden von Bildern zu optimieren. Standardmäßig ändert Glide die Größe und schneidet Bilder, um sie an die Zielansicht anzupassen. “

Um das Problem zu beheben, fügte ich der Initialisierung der Glide-Kette ohne Witze eine einzige Codezeile wie diese hinzu:

 Glide .with(target) .load(url) .apply( RequestOptions().dontTransform() // this line ) 

Sie sollten daher alle Glide-Transformationen für Bilder deaktivieren, wenn sie an einem gemeinsamen Übergang beteiligt sind.

Fehler Nr. 4. PostPostponeTransition () falsch verwalten


Ehrlich gesagt ist es nicht gerade ein Fehler, aber ich gehe trotzdem davon aus, dass es gut wäre, es zu erwähnen.

Wenn Sie die Methoden postPostponeTransition() und startPostponedEnterTransition() verwalten postPostponeTransition() , sollten Sie den richtigen Zeitpunkt auswählen. Der Moment ist, wenn die Benutzeroberfläche bereits gezeichnet werden soll.

Es gibt zwei Hauptpunkte, die wir kennen sollten, bevor wir die Methoden aufrufen:

  • Zum einen, wenn Bilder mit Übergang geladen und bereit sind
  • Zum anderen wird die Ansichtshierarchie gemessen und angelegt

Für Bilder verwenden wir normalerweise Glide und es hat einen ausgefallenen Hörer:

 RequestListener<Drawable> { override fun onLoadFailed( e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean ): Boolean { startPostponedEnterTransition() return false } override fun onResourceReady( resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean ): Boolean { startPostponedEnterTransition() return false } } 

Beachten Sie, dass wir in beiden Fällen startPostponedEnterTransition() aufrufen: Erfolg und Fehler. Es ist wichtig, denn wenn wir es nicht schaffen, friert Ihre Bewerbung ein.

In Fällen, in denen der Übergang ohne Bilder erfolgt, können doOnPreDraw() Erweiterungsmethode doOnPreDraw() von ktx im Stammlayout verwenden und dort startPostponedEnterTransition() .

BONUS : Apropos RecyclerView: Es reicht nicht aus, nur einen Listener für Bilder hinzuzufügen. Wir sollten eine Elementposition von RecyclerView beibehalten, von der aus der Übergang beginnt. Wenn der Benutzer zum vorherigen Bildschirm zurückkehrt, sollten wir die Bildladeposition mit der beibehaltenen Position beim Hörer vergleichen und den Übergang erst starten, wenn sie übereinstimmen.

Alles zusammenfügen


In diesem Artikel habe ich einige Fallstricke gezeigt, mit denen Sie möglicherweise konfrontiert sind, wenn Sie einen gemeinsamen Übergang mit Fragmenten implementieren und wie Sie damit umgehen können.

Kurz gesagt, hier sind sie:

  1. Beachten Sie die Fragmenthierarchie (übergeordnetes Fragment nicht vergessen)
  2. Erstellen Sie bei RecyclerView Übergangsnamen immer dynamisch (Quelle + Ziel).
  3. Deaktivieren Sie alle Glide-Transformationen
  4. Rufen Sie postPostponeTransition() und startPostponedEnterTransition() Bezug auf Ihre Logik korrekt auf.

Letzte Animation


Vielen Dank fürs Lesen und bis zum nächsten Mal!

PS Wenn Sie sich fragen, wie Sie Bildecken animieren können, fügen Sie hier den Code hinzu . Fügen Sie einfach den Rest der freigegebenen Animationsübergänge hinzu.

Beispiel
 fragment.sharedElementEnterTransition = TransitionSet().apply { addTransition(ChangeImageTransform()) addTransition(ChangeBounds()) addTransition(ChangeTransform()) addTransition(ChangeOutlineRadiusTransition(animationRadiusData.startRadius, animationRadiusData.endRadius) ) } 

Source: https://habr.com/ru/post/de463459/


All Articles