Android für Fortgeschrittene in Kotlin 03.2: Animation mit MotionLayout

1. Hinweis

Dieses Codelab ist Teil des Kurses „Advanced Android in Kotlin“. Sie können diesen Kurs am besten nutzen, wenn Sie die Codelabs der Reihe nach durcharbeiten. Das ist jedoch nicht zwingend erforderlich. Alle Codelabs des Kurses sind auf der Landingpage für Codelabs zu Advanced Android in Kotlin aufgeführt.

MotionLayout ist eine Bibliothek, mit der Sie Ihrer Android-App dynamische Animationen hinzufügen können. Sie basiert auf ConstraintLayout, und ermöglicht es Ihnen, alles zu animieren, was Sie mit ConstraintLayout erstellen können.

Mit MotionLayout können Sie die Position, Größe, Sichtbarkeit, den Alphawert, die Farbe, die Höhe, die Drehung und andere Attribute mehrerer Ansichten gleichzeitig animieren. Mit deklarativem XML können Sie koordinierte Animationen mit mehreren Ansichten erstellen, die im Code nur schwer zu realisieren sind.

Animationen sind eine gute Möglichkeit, die Nutzerfreundlichkeit einer App zu verbessern. Mit Animationen können Sie:

  • Änderungen anzeigen: Durch Animationen zwischen Status können Nutzer Änderungen in Ihrer Benutzeroberfläche auf natürliche Weise nachvollziehen.
  • Aufmerksamkeit erregen: Verwenden Sie Animationen, um die Aufmerksamkeit auf wichtige UI-Elemente zu lenken.
  • Ansprechende Designs erstellen: Durch effektive Animationen im Design wirken Apps professioneller.

Vorbereitung

Dieses Codelab richtet sich an Entwickler mit etwas Erfahrung in der Android-Entwicklung. Bevor Sie dieses Codelab durcharbeiten, sollten Sie Folgendes tun:

  • Sie wissen, wie Sie mit Android Studio eine App mit einer Aktivität und einem einfachen Layout erstellen und auf einem Gerät oder Emulator ausführen. Sie sollten sich mit ConstraintLayout vertraut machen. Weitere Informationen zu ConstraintLayout finden Sie im Codelab zu ConstraintLayout.

Aufgaben

  • Animation mit ConstraintSets und MotionLayout definieren
  • Animation basierend auf Drag-Ereignissen
  • Animation mit KeyPosition ändern
  • Attribute mit KeyAttribute ändern
  • Animationen mit Code ausführen
  • Ein- und ausblendbare Überschriften mit MotionLayout animieren

Voraussetzungen

  • Android Studio 4.0 (Der MotionLayout-Editor funktioniert nur mit dieser Version von Android Studio.)

2. Erste Schritte

So laden Sie die Beispiel-App herunter:

… oder klonen Sie das GitHub-Repository über die Befehlszeile mit dem folgenden Befehl:

$ git clone https://github.com/googlecodelabs/motionlayout.git

3. Animationen mit MotionLayout erstellen

Zuerst erstellen Sie eine Animation, die eine Ansicht als Reaktion auf Nutzerklicks vom oberen Rand des Bildschirms zum unteren Rand verschiebt.

Um eine Animation aus dem Startcode zu erstellen, benötigen Sie die folgenden wichtigen Elemente:

  • Eine MotionLayout,, die eine abgeleitete Klasse von ConstraintLayout ist. Alle Ansichten, die animiert werden sollen, werden im MotionLayout-Tag angegeben.
  • Eine MotionScene,, eine XML-Datei, die eine Animation für MotionLayout. beschreibt
  • Ein Transition,, das Teil des MotionScene ist und die Animationsdauer, den Trigger und die Art der Bewegung der Ansichten angibt.
  • Ein ConstraintSet, das sowohl die Start- als auch die Ende-Einschränkungen des Übergangs angibt.

Sehen wir uns diese der Reihe nach an, beginnend mit MotionLayout.

Schritt 1: Vorhandenen Code ansehen

MotionLayout ist eine abgeleitete Klasse von ConstraintLayout und unterstützt daher dieselben Funktionen, bietet aber zusätzlich Animationen. Wenn Sie MotionLayout verwenden möchten, fügen Sie eine MotionLayout-Ansicht hinzu, in der Sie ConstraintLayout. verwenden würden.

  1. Öffnen Sie in res/layout die Datei activity_step1.xml.. Sie sehen ein ConstraintLayout mit einem einzelnen ImageView eines Sterns, auf den eine Tönung angewendet wurde.

activity_step1.xml

<!-- initial code -->
<androidx.constraintlayout.widget.ConstraintLayout
       ...
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       >

   <ImageView
           android:id="@+id/red_star"
           ...
   />

</androidx.constraintlayout.motion.widget.MotionLayout>

Für diese ConstraintLayout gibt es keine Einschränkungen. Wenn Sie die App jetzt ausführen, werden die Sterne also ohne Einschränkungen angezeigt, d. h., sie werden an einer unbekannten Position platziert. Android Studio gibt eine Warnung aus, wenn keine Constraints vorhanden sind.

Schritt 2: In Motion Layout konvertieren

Wenn Sie MotionLayout, für Animationen verwenden möchten, müssen Sie die ConstraintLayout in eine MotionLayout konvertieren.

Damit in Ihrem Layout eine Bewegungsszene verwendet werden kann, muss darauf verwiesen werden.

  1. Öffnen Sie dazu die Designoberfläche. In Android Studio 4.0 öffnen Sie die Designoberfläche, indem Sie oben rechts das Symbol „Split“ oder „Design“ auswählen, wenn Sie eine Layout-XML-Datei ansehen.

a2beea710c2decb7.png

  1. Klicken Sie nach dem Öffnen der Designoberfläche mit der rechten Maustaste auf die Vorschau und wählen Sie In MotionLayout konvertieren aus.

4fa936a98a8393b9.png

Dadurch wird das ConstraintLayout-Tag durch ein MotionLayout-Tag ersetzt und dem MotionLayout-Tag wird ein motion:layoutDescription hinzugefügt, das auf @xml/activity_step1_scene. verweist.

activity_step1**.xml**

<!-- explore motion:layoutDescription="@xml/activity_step1_scene" -->
<androidx.constraintlayout.motion.widget.MotionLayout
       ...
       motion:layoutDescription="@xml/activity_step1_scene">

Eine Motion-Szene ist eine einzelne XML-Datei, in der eine Animation in einem MotionLayout beschrieben wird.

Sobald Sie die Umstellung auf MotionLayout vornehmen, wird im Designbereich der Bewegungseditor angezeigt.

66d0e80d5ab4daf8.png

Der Motion Editor enthält drei neue Elemente der Benutzeroberfläche:

  1. Übersicht: In diesem Modal können Sie verschiedene Teile der Animation auswählen. In diesem Bild ist die start ConstraintSet ausgewählt. Sie können den Übergang zwischen start und end auch auswählen, indem Sie auf den Pfeil dazwischen klicken.
  2. Abschnitt: Unter der Übersicht befindet sich ein Abschnittsfenster, das sich je nach dem aktuell ausgewählten Übersichtselement ändert. In diesem Bild werden die Informationen zu start ConstraintSet im Auswahlfenster angezeigt.
  3. Attribut: Im Attributbereich werden die Attribute des aktuell ausgewählten Elements aus dem Übersichts- oder Auswahlfenster angezeigt. Sie können sie dort auch bearbeiten. Auf diesem Bild sind die Attribute für start ConstraintSet zu sehen.

Schritt 3: Start- und Endbedingungen definieren

Alle Animationen können durch einen Start und ein Ende definiert werden. Der Start beschreibt, wie der Bildschirm vor der Animation aussieht, und das Ende, wie er nach Abschluss der Animation aussieht. MotionLayout ist dafür verantwortlich, wie die Animation zwischen dem Start- und dem Endstatus (im Zeitverlauf) erfolgt.

MotionScene verwendet ein ConstraintSet-Tag, um die Start- und Endstatus zu definieren. Ein ConstraintSet ist genau das, was der Name vermuten lässt: eine Reihe von Einschränkungen, die auf Ansichten angewendet werden können. Dazu gehören Breiten-, Höhen- und ConstraintLayout-Einschränkungen. Dazu gehören auch einige Attribute wie alpha. Sie enthält nicht die Ansichten selbst, sondern nur die Einschränkungen für diese Ansichten.

Alle in einer ConstraintSet-Datei angegebenen Einschränkungen überschreiben die in der Layoutdatei angegebenen Einschränkungen. Wenn Sie Einschränkungen sowohl im Layout als auch in der MotionScene definieren, werden nur die Einschränkungen in der MotionScene angewendet.

In diesem Schritt beschränken Sie die Sternenansicht so, dass sie oben auf dem Bildschirm beginnt und unten auf dem Bildschirm endet.

Sie können diesen Schritt entweder mit dem Motion Editor oder durch direktes Bearbeiten des Texts von activity_step1_scene.xml ausführen.

  1. Wählen Sie im Übersichtsfeld das start ConstraintSet aus.

6e57661ed358b860.png

  1. Wählen Sie im Bereich Auswahl die Option red_star aus. Derzeit wird „Quelle von layout“ angezeigt. Das bedeutet, dass die Quelle in dieser ConstraintSet nicht eingeschränkt ist. Klicken Sie oben rechts auf das Stiftsymbol, um eine Einschränkung zu erstellen.

f9564c574b86ea8.gif

  1. Prüfen Sie, ob für red_star die Quelle start angezeigt wird, wenn im Übersichtsbereich start ConstraintSet ausgewählt ist.
  2. Fügen Sie im Bereich „Attribute“ mit red_star in der start ConstraintSet eine Einschränkung oben hinzu. Klicken Sie dazu zuerst auf die blauen Schaltflächen +.

2fce076cd7b04bd.png

  1. Öffnen Sie xml/activity_step1_scene.xml, um den Code zu sehen, den der Motion Editor für diese Einschränkung generiert hat.

activity_step1_scene.xml

<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
   <Constraint
           android:id="@+id/red_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>

Das ConstraintSet hat ein id von @id/start und gibt alle Einschränkungen an, die auf alle Ansichten im MotionLayout angewendet werden sollen. Da diese MotionLayout nur eine Datenansicht hat, ist nur ein Constraint erforderlich.

Die Constraint in ConstraintSet gibt die ID der Ansicht an, die eingeschränkt wird, @id/red_star, die in activity_step1.xml definiert ist. Constraint-Tags geben nur Einschränkungen und Layoutinformationen an. Das Constraint-Tag weiß nicht, dass es auf ein ImageView angewendet wird.

Diese Einschränkung gibt die Höhe, Breite und die beiden anderen Einschränkungen an, die erforderlich sind, um die red_star-Ansicht auf den oberen Anfang des übergeordneten Elements zu beschränken.

  1. Wählen Sie im Übersichtsbereich das end ConstraintSet aus.

346e1248639b6f1e.png

  1. Führen Sie dieselben Schritte wie zuvor aus, um dem end ConstraintSet eine Constraint für red_star hinzuzufügen.
  2. Wenn Sie den Motion Editor für diesen Schritt verwenden möchten, fügen Sie dem bottom und dem end eine Einschränkung hinzu, indem Sie auf die blauen +-Schaltflächen klicken.

fd33c779ff83c80a.png

  1. Der Code in XML sieht so aus:

activitiy_step1_scene.xml

<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
   <Constraint
           android:id="@+id/red_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>

Wie bei @id/start hat dieses ConstraintSet ein einzelnes Constraint auf @id/red_star. Dieses Mal wird es auf den unteren Bildschirmrand beschränkt.

Sie müssen sie nicht @id/start und @id/end nennen, aber es ist praktisch, dies zu tun.

Schritt 4: Übergang definieren

Jede MotionScene muss außerdem mindestens einen Übergang enthalten. Eine Übergangsanimation definiert jeden Teil einer Animation, vom Anfang bis zum Ende.

Für einen Übergang muss ein Start- und End-ConstraintSet angegeben werden. Mit einem Übergang kann auch angegeben werden, wie die Animation auf andere Weise geändert werden soll, z. B. wie lange sie ausgeführt werden soll oder wie Ansichten durch Ziehen animiert werden sollen.

  1. Motion Editor hat beim Erstellen der MotionScene-Datei standardmäßig eine Übergangsanimation für uns erstellt. Öffnen Sie activity_step1_scene.xml, um den generierten Übergang zu sehen.

activity_step1_scene.xml

<!-- A transition describes an animation via start and end state -->
<Transition
   motion:constraintSetEnd="@+id/end"
   motion:constraintSetStart="@id/start"
   motion:duration="1000">
  <KeyFrameSet>
  </KeyFrameSet>
</Transition>

Das ist alles, was MotionLayout zum Erstellen einer Animation benötigt. Sehen wir uns die einzelnen Attribute an:

  • constraintSetStart wird auf die Aufrufe angewendet, sobald die Animation beginnt.
  • constraintSetEnd wird auf die Ansichten am Ende der Animation angewendet.
  • duration gibt an, wie lange die Animation in Millisekunden dauern soll.

MotionLayout berechnet dann einen Pfad zwischen den Start- und Endbedingungen und animiert ihn für die angegebene Dauer.

Schritt 5: Vorschau der Animation im Motion Editor ansehen

dff9ecdc1f4a0740.gif

Animation:Video, in dem eine Übergangsvorschau im Motion Editor wiedergegeben wird

  1. Öffnen Sie den Motion Editor und wählen Sie den Übergang aus, indem Sie im Übersichtsbereich auf den Pfeil zwischen start und end klicken.

1dc541ae8c43b250.png

  1. Im Bereich Auswahl werden Wiedergabesteuerelemente und eine Scrubbing-Leiste angezeigt, wenn eine Übergang ausgewählt ist. Klicken Sie auf „Wiedergabe“ oder ziehen Sie die aktuelle Position, um sich die Animation anzusehen.

a0fd2593384dfb36.png

Schritt 6: On-Click-Handler hinzufügen

Sie benötigen eine Möglichkeit, die Animation zu starten. Eine Möglichkeit hierfür ist, das MotionLayout auf Click-Events auf @id/red_star reagieren zu lassen.

  1. Öffnen Sie den Bewegungseditor und wählen Sie den Übergang aus, indem Sie im Übersichtsbereich auf den Pfeil zwischen Start und Ende klicken.

b6f94b344ce65290.png

  1. Klicken Sie in der Symbolleiste für das Übersichtspanel auf 699f7ae04024ccf6.png Klick- oder Wisch-Handler erstellen . Dadurch wird ein Handler hinzugefügt, der einen Übergang startet.
  2. Wählen Sie im Pop-up-Fenster Klick-Handler aus.

ccf92d06335105fe.png

  1. Ändern Sie Ansicht zum Klicken in red_star.

b0d3f0c970604f01.png

  1. Klicken Sie auf Hinzufügen. Der Click-Handler wird im Bewegungseditor durch einen kleinen Punkt auf dem Übergang dargestellt.

cec3913e67fb4105.png

  1. Fügen Sie dem OnClick-Handler, den Sie gerade im Attributbereich hinzugefügt haben, ein clickAction-Attribut mit dem Wert toggle hinzu.

9af6fc60673d093d.png

  1. Öffnen Sie activity_step1_scene.xml, um den vom Motion Editor generierten Code zu sehen.

activity_step1_scene.xml

<!-- A transition describes an animation via start and end state -->
<Transition
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">
    <!-- MotionLayout will handle clicks on @id/red_star to "toggle" the animation between the start and end -->
    <OnClick
        motion:targetId="@id/red_star"
        motion:clickAction="toggle" />
</Transition>

Mit dem Transition-Tag wird MotionLayout angewiesen, die Animation als Reaktion auf Klickereignisse mit einem <OnClick>-Tag auszuführen. Sehen wir uns die einzelnen Attribute an:

  • targetId ist die Ansicht, in der Sie nach Klicks suchen müssen.
  • clickAction von toggle wechselt bei einem Klick zwischen dem Start- und dem Endzustand. Weitere Optionen für clickAction
  1. Führen Sie den Code aus, klicken Sie auf Schritt 1 und dann auf den roten Stern, um die Animation zu sehen.

Schritt 5: Animationen in Aktion

Führen Sie die App aus. Wenn Sie auf den Stern klicken, sollte die Animation abgespielt werden.

7ba88af963fdfe10.gif

Die Datei mit der abgeschlossenen Bewegungsszene definiert einen Transition, der auf einen Start- und End-ConstraintSet verweist.

Zu Beginn der Animation (@id/start) ist das Sternsymbol an der oberen linken Ecke des Bildschirms fixiert. Am Ende der Animation (@id/end) ist das Sternsymbol am unteren Rand des Bildschirms fixiert.

<?xml version="1.0" encoding="utf-8"?>

<!-- Describe the animation for activity_step1.xml -->
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:android="http://schemas.android.com/apk/res/android">
   <!-- A transition describes an animation via start and end state -->
   <Transition
           motion:constraintSetStart="@+id/start"
           motion:constraintSetEnd="@+id/end"
           motion:duration="1000">
       <!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
       <OnClick
               motion:targetId="@id/red_star"
               motion:clickAction="toggle" />
   </Transition>

   <!-- Constraints to apply at the end of the animation -->
   <ConstraintSet android:id="@+id/start">
       <Constraint
               android:id="@+id/red_star"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               motion:layout_constraintStart_toStartOf="parent"
               motion:layout_constraintTop_toTopOf="parent" />
   </ConstraintSet>

   <!-- Constraints to apply at the end of the animation -->
   <ConstraintSet android:id="@+id/end">
       <Constraint
               android:id="@+id/red_star"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               motion:layout_constraintEnd_toEndOf="parent"
               motion:layout_constraintBottom_toBottomOf="parent" />
   </ConstraintSet>
</MotionScene>

4. Animationen basierend auf Drag-Ereignissen

In diesem Schritt erstellen Sie eine Animation, die auf ein Drag-Ereignis des Nutzers (wenn der Nutzer über den Bildschirm wischt) reagiert, um die Animation auszuführen. MotionLayout unterstützt das Erfassen von Touch-Ereignissen zum Verschieben von Ansichten sowie physikbasierte Wischbewegungen, um die Bewegung flüssig zu gestalten.

Schritt 1: Ursprünglichen Code prüfen

  1. Öffnen Sie dazu die Layoutdatei activity_step2.xml, die bereits ein MotionLayout enthält. Sehen Sie sich den Code an.

activity_step2.xml

<!-- initial code -->

<androidx.constraintlayout.motion.widget.MotionLayout
       ...
       motion:layoutDescription="@xml/step2" >

   <ImageView
           android:id="@+id/left_star"
           ...
   />

   <ImageView
           android:id="@+id/right_star"
           ...
   />

   <ImageView
           android:id="@+id/red_star"
           ...
   />

   <TextView
           android:id="@+id/credits"
           ...
           motion:layout_constraintTop_toTopOf="parent"
           motion:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.motion.widget.MotionLayout>

In diesem Layout werden alle Ansichten für die Animation definiert. Die drei Sternsymbole sind im Layout nicht eingeschränkt, da sie in der Bewegungsszene animiert werden.

Für die Credits TextView gelten Einschränkungen, da sie während der gesamten Animation an derselben Stelle bleiben und keine Attribute geändert werden.

Schritt 2: Szene animieren

Wie bei der letzten Animation wird die Animation durch einen Start- und einen End-ConstraintSet, und ein Transition definiert.

Definieren Sie das ConstraintSet für den Start.

  1. Öffnen Sie die Bewegungsszene xml/step2.xml, um die Animation zu definieren.
  2. Fügen Sie die Einschränkungen für die Startbedingung start hinzu. Zu Beginn sind alle drei Sterne unten in der Mitte des Bildschirms zu sehen. Die Sterne rechts und links haben den alpha-Wert 0.0. Das bedeutet, dass sie vollständig transparent und ausgeblendet sind.

step2.xml

<!-- TODO apply starting constraints -->

<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
   <Constraint
           android:id="@+id/red_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintBottom_toBottomOf="parent" />

   <Constraint
           android:id="@+id/left_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:alpha="0.0"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintBottom_toBottomOf="parent" />

   <Constraint
           android:id="@+id/right_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:alpha="0.0"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>

In diesem ConstraintSet geben Sie für jeden Stern ein Constraint an. Jede Einschränkung wird von MotionLayout zu Beginn der Animation angewendet.

Jede Sternansicht wird mithilfe von Start-, End- und unteren Einschränkungen unten auf dem Bildschirm zentriert. Die beiden Sterne @id/left_star und @id/right_star haben beide einen zusätzlichen Alphawert, der sie unsichtbar macht und der zu Beginn der Animation angewendet wird.

Die Einschränkungssätze start und end definieren den Anfang und das Ende der Animation. Eine Einschränkung für den Start, z. B. motion:layout_constraintStart_toStartOf, legt den Start einer Ansicht auf den Start einer anderen Ansicht fest. Das kann anfangs verwirrend sein, da der Name start sowohl für als auch für verwendet wird und beide im Kontext von Einschränkungen verwendet werden. Um die Unterscheidung zu verdeutlichen, bezieht sich start in layout_constraintStart auf den „Start“ der Ansicht, also links bei linksläufigen Sprachen und rechts bei rechtsläufigen Sprachen. Der start-Einschränkungssatz bezieht sich auf den Beginn der Animation.

End-ConstraintSet definieren

  1. Definieren Sie die Endbeschränkung, um alle drei Sterne mit einer Kette unter @id/credits zu positionieren. Außerdem wird der Endwert von alpha der linken und rechten Sterne auf 1.0 gesetzt.

step2.xml

<!-- TODO apply ending constraints -->

<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">

   <Constraint
           android:id="@+id/left_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:alpha="1.0"
           motion:layout_constraintHorizontal_chainStyle="packed"
           motion:layout_constraintStart_toStartOf="parent"
           motion:layout_constraintEnd_toStartOf="@id/red_star"
           motion:layout_constraintTop_toBottomOf="@id/credits" />

   <Constraint
           android:id="@+id/red_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           motion:layout_constraintStart_toEndOf="@id/left_star"
           motion:layout_constraintEnd_toStartOf="@id/right_star"
           motion:layout_constraintTop_toBottomOf="@id/credits" />

   <Constraint
           android:id="@+id/right_star"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:alpha="1.0"
           motion:layout_constraintStart_toEndOf="@id/red_star"
           motion:layout_constraintEnd_toEndOf="parent"
           motion:layout_constraintTop_toBottomOf="@id/credits" />
</ConstraintSet>

Das Ergebnis ist, dass sich die Ansichten beim Animieren vom Zentrum aus nach außen und oben ausbreiten.

Da die alpha-Property in beiden ConstraintSets für @id/right_start und @id/left_star festgelegt ist, werden beide Ansichten im Laufe der Animation eingeblendet.

Animation basierend auf dem Wischen des Nutzers

Mit MotionLayout können Nutzer-Drag-Ereignisse oder ein Wischen erfasst werden, um eine physikbasierte „Schleuder“-Animation zu erstellen. Das bedeutet, dass die Ansichten weiterlaufen, wenn der Nutzer sie schleudert, und sich verlangsamen, wie es bei einem physischen Objekt der Fall wäre, das über eine Oberfläche rollt. Sie können diese Art von Animation mit einem OnSwipe-Tag im Transition hinzufügen.

  1. Ersetzen Sie das TODO für das Hinzufügen eines OnSwipe-Tags durch <OnSwipe motion:touchAnchorId="@id/red_star" />.

step2.xml

<!-- TODO add OnSwipe tag -->

<!-- A transition describes an animation via start and end state -->
<Transition
       motion:constraintSetStart="@+id/start"
       motion:constraintSetEnd="@+id/end">
   <!-- MotionLayout will track swipes relative to this view -->
   <OnSwipe motion:touchAnchorId="@id/red_star" />
</Transition>

OnSwipe enthält einige Attribute, wobei touchAnchorId das wichtigste ist.

  • touchAnchorId ist die verfolgte Ansicht, die sich als Reaktion auf Berührungen bewegt. MotionLayout hält diese Ansicht auf demselben Abstand zum Finger, der wischt.
  • Mit touchAnchorSide wird festgelegt, welche Seite der Ansicht erfasst werden soll. Das ist wichtig für Ansichten, die ihre Größe ändern, komplexen Pfaden folgen oder bei denen sich eine Seite schneller bewegt als die andere.
  • dragDirection bestimmt, welche Richtung für diese Animation wichtig ist (oben, unten, links oder rechts).

Wenn MotionLayout auf Ziehereignisse wartet, wird der Listener für die MotionLayout-Ansicht und nicht für die von touchAnchorId angegebene Ansicht registriert. Wenn ein Nutzer eine Geste an einer beliebigen Stelle auf dem Bildschirm beginnt, hält MotionLayout den Abstand zwischen seinem Finger und dem touchAnchorSide der touchAnchorId-Ansicht konstant. Wenn sie beispielsweise 100 dp von der Ankerseite entfernt sind, hält MotionLayout diese Seite während der gesamten Animation 100 dp von ihrem Finger entfernt.

Jetzt ausprobieren

  1. Führen Sie die App noch einmal aus und öffnen Sie den Bildschirm „Schritt 2“. Die Animation wird angezeigt.
  2. Probieren Sie aus, Ihren Finger während der Animation loszulassen, um zu sehen, wie MotionLayout flüssigkeitsbasierte Animationen darstellt.

fefcdd690a0dcaec.gif

Mit MotionLayout lassen sich sehr unterschiedliche Designs animieren. Dabei können Sie die Funktionen von ConstraintLayout nutzen, um ansprechende Effekte zu erzielen.

In dieser Animation sind alle drei Ansichten zu Beginn relativ zu ihrem übergeordneten Element unten auf dem Bildschirm positioniert. Am Ende werden die drei Ansichten relativ zu @id/credits in einer Kette positioniert.

Trotz dieser sehr unterschiedlichen Layouts wird mit MotionLayout eine flüssige Animation zwischen Start und Ende erstellt.

5. Pfad ändern

In diesem Schritt erstellen Sie eine Animation, die einem komplexen Pfad folgt und die Credits während der Bewegung animiert. Mit MotionLayout kann der Pfad geändert werden, den eine Ansicht zwischen dem Start und dem Ende nimmt. Dazu wird ein KeyPosition verwendet.

Schritt 1: Vorhandenen Code ansehen

  1. Öffnen Sie layout/activity_step3.xml und xml/step3.xml, um das vorhandene Layout und die Bewegungsszene zu sehen. In ImageView und TextView werden der Mond und der Abspanntext angezeigt.
  2. Öffnen Sie die Datei mit der Bewegungsszene (xml/step3.xml). Sie sehen, dass ein Transition von @id/start nach @id/end definiert ist. In der Animation wird das Mondbild mit zwei ConstraintSets von links unten nach rechts unten auf dem Bildschirm verschoben. Der Abspanntext wird von alpha="0.0" bis alpha="1.0" eingeblendet, während sich der Mond bewegt.
  3. Führen Sie die App jetzt aus und wählen Sie Schritt 3 aus. Wenn Sie auf den Mond klicken, sehen Sie, dass er einem linearen Pfad (oder einer geraden Linie) vom Start bis zum Ende folgt.

Schritt 2: Pfad-Debugging aktivieren

Bevor Sie der Bewegung des Mondes einen Bogen hinzufügen, sollten Sie das Pfad-Debugging in MotionLayout aktivieren.

Um komplexe Animationen mit MotionLayout zu entwickeln, können Sie den Animationspfad jeder Ansicht zeichnen. Das ist hilfreich, wenn Sie Ihre Animation visualisieren und die kleinen Details der Bewegung optimieren möchten.

  1. Wenn Sie Debugging-Pfade aktivieren möchten, öffnen Sie layout/activity_step3.xml und fügen Sie dem MotionLayout-Tag motion:motionDebug="SHOW_PATH" hinzu.

activity_step3.xml

<!-- Add motion:motionDebug="SHOW_PATH" -->

<androidx.constraintlayout.motion.widget.MotionLayout
       ...
       motion:motionDebug="SHOW_PATH" >

Wenn Sie das Pfad-Debugging aktiviert haben und die App noch einmal ausführen, werden die Pfade aller Ansichten mit einer gepunkteten Linie visualisiert.

23bbb604f456f65c.png

  • Kreise stellen die Start- oder Endposition einer Ansicht dar.
  • Linien stellen den Pfad einer Ansicht dar.
  • Rauten stellen einen KeyPosition dar, der den Pfad ändert.

In dieser Animation ist der mittlere Kreis beispielsweise die Position des Abspanntexts.

Schritt 3: Pfad ändern

Alle Animationen in MotionLayout werden durch einen Start- und einen Endpunkt ConstraintSet definiert, der festlegt, wie der Bildschirm vor Beginn und nach Abschluss der Animation aussieht. Standardmäßig wird mit MotionLayout ein linearer Pfad (eine gerade Linie) zwischen der Start- und der Endposition jeder Ansicht gezeichnet, deren Position sich ändert.

Um komplexe Pfade wie den Bogen des Mondes in diesem Beispiel zu erstellen, verwendet MotionLayout ein KeyPosition, um den Pfad zu ändern, den eine Ansicht zwischen Start und Ende nimmt.

  1. Öffnen Sie xml/step3.xml und fügen Sie der Szene ein KeyPosition hinzu. Das KeyPosition-Tag wird innerhalb des Transition-Tags platziert.

eae4dae9a12d0410.png

step3.xml

<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
   <KeyPosition
           motion:framePosition="50"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.5"
   />
</KeyFrameSet>

Ein KeyFrameSet ist ein untergeordnetes Element eines Transition und eine Gruppe aller KeyFrames, z. B. KeyPosition, die während des Übergangs angewendet werden sollen.

Da MotionLayout den Pfad für den Mond zwischen dem Start und dem Ende berechnet, wird der Pfad basierend auf dem in KeyFrameSet angegebenen KeyPosition geändert. Wenn Sie die App noch einmal ausführen, sehen Sie, wie sich der Pfad ändert.

Ein KeyPosition-Objekt hat mehrere Attribute, die beschreiben, wie der Pfad geändert wird. Die wichtigsten sind:

  • framePosition ist eine Zahl zwischen 0 und 100. Damit wird definiert, wann dieser KeyPosition in der Animation angewendet werden soll. Der Wert 1 entspricht 1% der Animation und der Wert 99 entspricht 99% der Animation. Wenn der Wert also 50 ist, wird er genau in der Mitte angewendet.
  • motionTarget ist die Ansicht, für die mit diesem KeyPosition der Pfad geändert wird.
  • keyPositionType zeigt, wie der Pfad durch KeyPosition geändert wird. Er kann entweder parentRelative, pathRelative oder deltaRelative sein (wie im nächsten Schritt erläutert).
  • percentX | percentY gibt an, wie stark der Pfad bei framePosition geändert werden soll (Werte zwischen 0,0 und 1,0, wobei negative Werte und Werte > 1 zulässig sind).

Sie können sich das so vorstellen: „Ändere bei framePosition den Pfad von motionTarget durch Verschieben um percentX oder percentY entsprechend den von keyPositionTypeermittelten Koordinaten.“

Standardmäßig werden alle Ecken, die durch das Ändern des Pfads entstehen, durch MotionLayout abgerundet. Wenn Sie sich die gerade erstellte Animation ansehen, können Sie erkennen, dass der Mond in der Kurve einem gekrümmten Pfad folgt. In den meisten Fällen ist das auch gewünscht. Andernfalls können Sie das Attribut curveFit angeben, um die Animation anzupassen.

Jetzt ausprobieren

Wenn Sie die App noch einmal ausführen, wird die Animation für diesen Schritt angezeigt.

46b179c01801f19e.gif

Der Mond folgt einem Bogen, da er einen in der Transition angegebenen KeyPosition durchläuft.

<KeyPosition
       motion:framePosition="50"
       motion:motionTarget="@id/moon"
       motion:keyPositionType="parentRelative"
       motion:percentY="0.5"
/>

Sie können KeyPosition so lesen: „Ändere bei framePosition 50 (auf halbem Weg durch die Animation) den Pfad von motionTarget @id/moon, indem du ihn um 50% Y (auf halbem Weg nach unten auf dem Bildschirm) entsprechend den Koordinaten verschiebst, die durch parentRelative (die gesamte MotionLayout) bestimmt werden.“

In der Mitte der Animation muss der Mond also durch einen KeyPosition gehen, der 50% unter dem oberen Bildschirmrand liegt. Diese KeyPosition ändert die X-Bewegung überhaupt nicht. Der Mond bewegt sich also weiterhin horizontal von Anfang bis Ende. MotionLayout berechnet einen glatten Pfad, der durch diesen KeyPosition verläuft, während er sich zwischen Start und Ende bewegt.

Wenn Sie genau hinsehen, wird der Text für die Credits durch die Position des Mondes eingeschränkt. Warum bewegt es sich nicht auch vertikal?

1c7cf779931e45cc.gif

<Constraint
       android:id="@id/credits"
       ...
       motion:layout_constraintBottom_toBottomOf="@id/moon"
       motion:layout_constraintTop_toTopOf="@id/moon"
/>

Obwohl du den Pfad des Mondes änderst, bewegen sich die Start- und Endpositionen des Mondes vertikal überhaupt nicht. KeyPosition ändert die Start- oder Endposition nicht, sodass der Abspanntext auf die endgültige Endposition des Mondes beschränkt ist.

Wenn Sie möchten, dass sich der Abspann mit dem Mond bewegt, können Sie dem Abspann ein KeyPosition hinzufügen oder die Startbedingungen für @id/credits ändern.

Im nächsten Abschnitt werden die verschiedenen Arten von keyPositionType in MotionLayout beschrieben.

6. keyPositionType

Im letzten Schritt haben Sie den keyPosition-Typ parentRelative verwendet, um den Pfad um 50% des Bildschirms zu versetzen. Das Attribut keyPositionType bestimmt, wie MotionLayout den Pfad gemäß percentX oder percentY ändert.

<KeyFrameSet>
   <KeyPosition
           motion:framePosition="50"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.5"
   />
</KeyFrameSet>

Es gibt drei verschiedene Arten von keyPosition: parentRelative, pathRelative und deltaRelative. Wenn Sie einen Typ angeben, ändert sich das Koordinatensystem, anhand dessen percentX und percentY berechnet werden.

Was ist ein Koordinatensystem?

Ein Koordinatensystem bietet eine Möglichkeit, einen Punkt im Raum anzugeben. Sie sind auch nützlich, um eine Position auf dem Bildschirm zu beschreiben.

MotionLayout-Koordinatensysteme sind kartesische Koordinatensysteme. Das bedeutet, dass sie eine X- und eine Y-Achse haben, die durch zwei senkrechte Linien definiert werden. Der Hauptunterschied zwischen den beiden besteht darin, wo sich die X-Achse auf dem Bildschirm befindet (die Y-Achse verläuft immer senkrecht zur X-Achse).

Alle Koordinatensysteme in MotionLayout verwenden Werte zwischen 0.0 und 1.0 auf der X- und der Y-Achse. Sie lassen negative Werte und Werte zu, die größer als 1.0 sind. Ein percentX-Wert von -2.0 würde beispielsweise bedeuten, dass Sie sich zweimal in die entgegengesetzte Richtung der X-Achse bewegen.

Wenn sich das alles ein bisschen zu sehr nach Algebraunterricht anhört, sehen Sie sich die Bilder unten an.

parentRelative-Koordinaten

a7b7568d46d9dec7.png

Die keyPositionType von parentRelative verwendet dasselbe Koordinatensystem wie der Bildschirm. Dabei wird (0, 0) oben links und (1, 1) unten rechts im gesamten MotionLayout definiert.

Sie können parentRelative verwenden, wenn Sie eine Animation erstellen möchten, die sich über den gesamten MotionLayout erstreckt, wie der Mondbogen in diesem Beispiel.

Wenn Sie einen Pfad jedoch relativ zur Bewegung ändern möchten, z. B. ihn leicht krümmen, sind die anderen beiden Koordinatensysteme besser geeignet.

deltaRelative-Koordinaten

5680bf553627416c.png

Delta ist ein mathematischer Begriff für Änderung. deltaRelative bedeutet also „relative Änderung“. In deltaRelative-Koordinaten(0,0) ist die Startposition der Ansicht und (1,1) die Endposition. Die X- und Y-Achsen sind am Bildschirm ausgerichtet.

Die X-Achse ist immer horizontal auf dem Bildschirm und die Y-Achse immer vertikal. Im Vergleich zu parentRelative besteht der Hauptunterschied darin, dass die Koordinaten nur den Teil des Bildschirms beschreiben, in dem sich die Ansicht bewegt.

deltaRelative ist ein hervorragendes Koordinatensystem, um die horizontale oder vertikale Bewegung isoliert zu steuern. Sie könnten beispielsweise eine Animation erstellen, bei der die vertikale (Y-)Bewegung bei 50 % abgeschlossen ist und die horizontale (X-)Bewegung fortgesetzt wird.

Pfadrelative Koordinaten

f3aaadaac8b4a93f.png

Das letzte Koordinatensystem in MotionLayout ist pathRelative. Sie unterscheidet sich deutlich von den anderen beiden, da die X-Achse dem Bewegungspfad vom Start bis zum Ende folgt. (0,0) ist also die Startposition und (1,0) die Endposition.

Warum sollten Sie das tun? Das ist auf den ersten Blick ziemlich überraschend, zumal dieses Koordinatensystem nicht einmal am Bildschirmkoordinatensystem ausgerichtet ist.

pathRelative ist für einige Dinge wirklich nützlich.

  • Ansicht während eines Teils der Animation beschleunigen, verlangsamen oder anhalten: Da die X-Dimension immer genau dem Pfad entspricht, den die Ansicht nimmt, können Sie mit pathRelative KeyPosition ändern, bei welchem framePosition ein bestimmter Punkt auf diesem Pfad erreicht wird. Bei einem KeyPosition bei framePosition="50" mit einem percentX="0.1" würde die Animation für die ersten 10% der Bewegung 50% der Zeit benötigen.
  • Einem Pfad einen subtilen Bogen hinzufügen Da die Y-Dimension immer senkrecht zur Bewegung verläuft, wird durch Ändern von Y der Pfad relativ zur Gesamtbewegung gekrümmt.
  • Eine zweite Dimension hinzufügendeltaRelative funktioniert nicht. Bei rein horizontalen und vertikalen Bewegungen wird mit deltaRelative nur eine nützliche Dimension erstellt. pathRelative erstellt jedoch immer nutzbare X- und Y-Koordinaten.

Im nächsten Schritt erfahren Sie, wie Sie noch komplexere Pfade mit mehr als einem KeyPosition erstellen.

7. Komplexe Pfade erstellen

Die Animation, die Sie im letzten Schritt erstellt haben, hat eine glatte Kurve, aber die Form könnte eher wie ein Mond aussehen.

Pfad mit mehreren KeyPosition-Elementen ändern

Mit MotionLayout kann ein Pfad weiter angepasst werden, indem beliebig viele KeyPosition definiert werden, um eine beliebige Bewegung zu erzielen. Für diese Animation erstellen Sie einen Bogen. Sie könnten den Mond aber auch in der Mitte des Bildschirms auf und ab springen lassen.

  1. Öffnen Sie xml/step4.xml. Es hat dieselben Aufrufe und die KeyFrame, die Sie im letzten Schritt hinzugefügt haben.
  2. Um die Spitze der Kurve abzurunden, fügen Sie dem Pfad von @id/moon zwei weitere KeyPositions hinzu, eine kurz vor und eine kurz nach dem Erreichen des höchsten Punkts.

500b5ac2db48ef87.png

step4.xml

<!-- TODO: Add two more KeyPositions to the KeyFrameSet here -->
<KeyPosition
       motion:framePosition="25"
       motion:motionTarget="@id/moon"
       motion:keyPositionType="parentRelative"
       motion:percentY="0.6"
/>
<KeyPosition
       motion:framePosition="75"
       motion:motionTarget="@id/moon"
       motion:keyPositionType="parentRelative"
       motion:percentY="0.6"
/>

Diese KeyPositions werden bei 25% und 75% der Animation angewendet. Dadurch bewegt sich @id/moon auf einem Pfad, der 60% vom oberen Bildschirmrand entfernt ist. In Kombination mit der vorhandenen KeyPosition von 50 % ergibt sich ein sanfter Bogen für die Bewegung des Mondes.

In MotionLayout können Sie so viele KeyPositions hinzufügen, wie Sie für den gewünschten Bewegungspfad benötigen. MotionLayout wendet jedes KeyPosition am angegebenen framePosition an und ermittelt, wie eine flüssige Bewegung durch alle KeyPositions erstellt werden kann.

Jetzt ausprobieren

  1. Führen Sie die App noch einmal aus. Schritt 4 zeigt die Animation in Aktion. Wenn Sie auf den Mond klicken, folgt er dem Pfad vom Start bis zum Ende und durchläuft dabei jedes KeyPosition, das in der KeyFrameSet angegeben wurde.

Selbst erkunden

Bevor Sie sich anderen Arten von KeyFrame zuwenden, sollten Sie dem KeyFrameSet einige weitere KeyPositions hinzufügen, um zu sehen, welche Effekte Sie nur mit KeyPosition erzielen können.

Hier ist ein Beispiel für das Erstellen eines komplexen Pfads, der sich während der Animation hin und her bewegt.

cd9faaffde3dfef.png

step4.xml

<!-- Complex paths example: Dancing moon -->
<KeyFrameSet>
   <KeyPosition
           motion:framePosition="25"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.6"
           motion:percentX="0.1"
   />
   <KeyPosition
           motion:framePosition="50"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.5"
           motion:percentX="0.3"
   />
   <KeyPosition
           motion:framePosition="75"
           motion:motionTarget="@id/moon"
           motion:keyPositionType="parentRelative"
           motion:percentY="0.6"
           motion:percentX="0.1"
   />
</KeyFrameSet>

Wenn Sie KeyPosition fertig erkundet haben, geht es im nächsten Schritt mit anderen Arten von KeyFrames weiter.

8. Attribute während der Bewegung ändern

Beim Erstellen dynamischer Animationen werden häufig die size-, rotation- oder alpha-Werte von Ansichten im Laufe der Animation geändert. Mit MotionLayout lassen sich viele Attribute in einer beliebigen Ansicht mit einem KeyAttribute animieren.

In diesem Schritt verwenden Sie KeyAttribute, um den Mond zu skalieren und zu drehen. Außerdem verwenden Sie ein KeyAttribute, um die Anzeige des Texts zu verzögern, bis der Mond seine Reise fast abgeschlossen hat.

Schritt 1: Größe und Drehung mit KeyAttribute anpassen

  1. Öffnen Sie xml/step5.xml, die dieselbe Animation enthält, die Sie im letzten Schritt erstellt haben. Auf diesem Bildschirm wird zur Abwechslung ein anderes Weltraumbild als Hintergrund verwendet.
  2. Damit sich der Mond vergrößert und dreht, fügen Sie zwei KeyAttribute-Tags in KeyFrameSet bei keyFrame="50" und keyFrame="100" ein.

bbae524a2898569.png

step5.xml

<!-- TODO: Add KeyAttributes to rotate and resize @id/moon -->

<KeyAttribute
       motion:framePosition="50"
       motion:motionTarget="@id/moon"
       android:scaleY="2.0"
       android:scaleX="2.0"
       android:rotation="-360"
/>
<KeyAttribute
       motion:framePosition="100"
       motion:motionTarget="@id/moon"
       android:rotation="-720"
/>

Diese KeyAttributes werden bei 50% und 100% der Animation angewendet. Die erste KeyAttribute bei 50% erfolgt am oberen Ende des Bogens. Dadurch wird die Ansicht verdoppelt und um -360 Grad (oder einen vollen Kreis) gedreht. Mit dem zweiten KeyAttribute wird die zweite Drehung auf -720 Grad (zwei volle Kreise) abgeschlossen und die Größe wird wieder auf den regulären Wert verkleinert, da die Werte scaleX und scaleY standardmäßig auf 1,0 festgelegt sind.

Wie bei einem KeyPosition werden bei einem KeyAttribute die framePosition und motionTarget verwendet, um anzugeben, wann die KeyFrame angewendet werden soll und welche Ansicht geändert werden soll. MotionLayout interpoliert zwischen KeyPositions, um flüssige Animationen zu erstellen.

KeyAttributes unterstützt Attribute, die auf alle Ansichten angewendet werden können. Sie unterstützen das Ändern von grundlegenden Attributen wie visibility, alpha oder elevation. Sie können die Drehung auch wie hier ändern, mit rotateX und rotateY in drei Dimensionen drehen, mit scaleX und scaleY die Größe skalieren oder die Position der Ansicht in X, Y oder Z verschieben.

Schritt 2: Abspann verzögern

Ziel dieses Schritts ist es, die Animation so zu aktualisieren, dass der Abspanntext erst erscheint, wenn die Animation fast abgeschlossen ist.

  1. Wenn Sie die Anzeige des Abspanns verzögern möchten, definieren Sie einen weiteren KeyAttribute, der dafür sorgt, dass alpha bis keyPosition="85" auf 0 bleibt. MotionLayout wird weiterhin reibungslos von 0 auf 100 % Alpha übergehen, allerdings erst in den letzten 15 % der Animation.

step5.xml

<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->

<KeyAttribute
       motion:framePosition="85"
       motion:motionTarget="@id/credits"
       android:alpha="0.0"
/>

Durch dieses KeyAttribute bleibt der alpha von @id/credits für die ersten 85% der Animation bei 0,0. Da der Alphawert am Anfang 0 ist, ist das Element in den ersten 85% der Animation nicht sichtbar.

Das Ergebnis dieser KeyAttribute ist, dass der Abspann gegen Ende der Animation angezeigt wird. Dadurch entsteht der Eindruck, dass sie mit dem Mond koordiniert sind, der sich in der rechten Ecke des Bildschirms niederlässt.

Wenn Sie Animationen in einer Ansicht verzögern, während sich eine andere Ansicht so bewegt, können Sie beeindruckende Animationen erstellen, die sich für den Nutzer dynamisch anfühlen.

Jetzt ausprobieren

  1. Führen Sie die App noch einmal aus und gehen Sie zu Schritt 5, um die Animation in Aktion zu sehen. Wenn Sie auf den Mond klicken, folgt er dem Pfad vom Start bis zum Ende und durchläuft dabei alle KeyAttribute, die in der KeyFrameSet angegeben wurden.

2f4bfdd681c1fa98.gif

Da Sie den Mond zweimal um die eigene Achse drehen, macht er nun einen doppelten Rückwärtssalto. Die Credits werden erst eingeblendet, wenn die Animation fast abgeschlossen ist.

Selbst erkunden

Bevor Sie mit dem letzten Typ von KeyFrame fortfahren, sollten Sie andere Standard-Attribute in der KeyAttributes ändern. Ändern Sie beispielsweise rotation in rotationX, um zu sehen, welche Animation dadurch erstellt wird.

Hier ist eine Liste der Standardattribute, die Sie ausprobieren können:

  • android:visibility
  • android:alpha
  • android:elevation
  • android:rotation
  • android:rotationX
  • android:rotationY
  • android:scaleX
  • android:scaleY
  • android:translationX
  • android:translationY
  • android:translationZ

9. Benutzerdefinierte Attribute ändern

Bei umfangreichen Animationen werden die Farbe oder andere Attribute einer Ansicht geändert. Während MotionLayout ein KeyAttribute verwenden kann, um eines der Standardattribute zu ändern, die in der vorherigen Aufgabe aufgeführt sind, verwenden Sie ein CustomAttribute, um ein beliebiges anderes Attribut anzugeben.

Mit einem CustomAttribute kann jeder Wert festgelegt werden, für den ein Setter vorhanden ist. Sie können beispielsweise die backgroundColor einer View mit einem CustomAttribute festlegen. MotionLayout verwendet reflection, um den Setter zu finden, und ruft ihn dann wiederholt auf, um die Ansicht zu animieren.

In diesem Schritt verwenden Sie ein CustomAttribute, um das Attribut colorFilter für den Mond festzulegen und die unten gezeigte Animation zu erstellen.

5fb6792126a09fda.gif

Benutzerdefinierte Attribute definieren

  1. Öffnen Sie zuerst xml/step6.xml. Sie enthält dieselbe Animation, die Sie im letzten Schritt erstellt haben.
  2. Damit der Mond die Farbe ändert, füge zwei KeyAttribute mit einem CustomAttribute im KeyFrameSet bei keyFrame="0", keyFrame="50" und keyFrame="100". ein.

214699d5fdd956da.png

step6.xml

<!-- TODO: Add Custom attributes here -->
<KeyAttribute
       motion:framePosition="0"
       motion:motionTarget="@id/moon">
   <CustomAttribute
           motion:attributeName="colorFilter"
           motion:customColorValue="#FFFFFF"
   />
</KeyAttribute>
<KeyAttribute
       motion:framePosition="50"
       motion:motionTarget="@id/moon">
   <CustomAttribute
           motion:attributeName="colorFilter"
           motion:customColorValue="#FFB612"
   />
</KeyAttribute>
<KeyAttribute
       motion:framePosition="100"
       motion:motionTarget="@id/moon">
   <CustomAttribute
           motion:attributeName="colorFilter"
           motion:customColorValue="#FFFFFF"
   />
</KeyAttribute>

Sie fügen ein CustomAttribute in ein KeyAttribute ein. Die CustomAttribute wird am framePosition angewendet, der von der KeyAttribute angegeben wird.

Im CustomAttribute müssen Sie eine attributeName und einen festzulegenden Wert angeben.

  • motion:attributeName ist der Name des Setters, der von diesem benutzerdefinierten Attribut aufgerufen wird. In diesem Beispiel wird setColorFilter für Drawable aufgerufen.
  • motion:custom*Value ist ein benutzerdefinierter Wert des im Namen angegebenen Typs. In diesem Beispiel ist der benutzerdefinierte Wert eine angegebene Farbe.

Benutzerdefinierte Werte können einen der folgenden Typen haben:

  • Farbe
  • Ganzzahl
  • Gleitkommazahl
  • String
  • Dimension
  • Boolesch

Mit dieser API kann MotionLayout alles animieren, was einen Setter für eine beliebige Ansicht bietet.

Jetzt ausprobieren

  1. Führen Sie die App noch einmal aus und gehen Sie zu Schritt 6, um die Animation in Aktion zu sehen. Wenn Sie auf den Mond klicken, folgt er dem Pfad vom Start bis zum Ende und durchläuft dabei alle KeyAttribute, die in der KeyFrameSet angegeben wurden.

5fb6792126a09fda.gif

Wenn Sie weitere KeyFrames hinzufügen, ändert MotionLayout den Pfad des Mondes von einer geraden Linie zu einer komplexen Kurve. Außerdem werden ein doppelter Rückwärtssalto, eine Größenänderung und ein Farbwechsel in der Mitte der Animation hinzugefügt.

In echten Animationen werden oft mehrere Ansichten gleichzeitig animiert, wobei ihre Bewegung entlang verschiedener Pfade und Geschwindigkeiten gesteuert wird. Wenn Sie für jede Ansicht ein anderes KeyFrame angeben, können Sie komplexe Animationen erstellen, bei denen mehrere Ansichten mit MotionLayout animiert werden.

10. Drag-Ereignisse und komplexe Pfade

In diesem Schritt erfahren Sie, wie Sie OnSwipe mit komplexen Pfaden verwenden. Bisher wurde die Animation des Mondes durch einen OnClick-Listener ausgelöst und lief für eine bestimmte Dauer.

Um Animationen mit komplexen Pfaden wie die Mondanimation, die Sie in den letzten Schritten erstellt haben, mit OnSwipe zu steuern, müssen Sie wissen, wie OnSwipe funktioniert.

Schritt 1: OnSwipe-Verhalten untersuchen

  1. Öffnen Sie xml/step7.xml und suchen Sie nach der vorhandenen OnSwipe-Deklaration.

step7.xml

<!-- Fix OnSwipe by changing touchAnchorSide →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
/>
  1. Führen Sie die App auf Ihrem Gerät aus und fahren Sie mit Schritt 7 fort. Versuchen Sie, eine flüssige Animation zu erstellen, indem Sie den Mond entlang des Bogenpfads ziehen.

Wenn Sie diese Animation ausführen, sieht sie nicht sehr gut aus. Nachdem der Mond den höchsten Punkt des Bogens erreicht hat, beginnt er, sich unregelmäßig zu bewegen.

ed96e3674854a548.gif

Um den Fehler zu verstehen, sehen Sie sich an, was passiert, wenn der Nutzer den Bogen kurz unter dem oberen Ende berührt. Da das OnSwipe-Tag ein motion:touchAnchorSide="bottom" hat, versucht MotionLayout, den Abstand zwischen dem Finger und dem unteren Rand der Ansicht während der gesamten Animation konstant zu halten.

Da sich der untere Teil des Mondes aber nicht immer in dieselbe Richtung bewegt, sondern erst nach oben und dann wieder nach unten, weiß MotionLayout nicht, was zu tun ist, wenn der Nutzer gerade den höchsten Punkt des Bogens passiert hat. Wenn Sie den unteren Teil des Mondes erfassen, wo sollte er platziert werden, wenn der Nutzer ihn berührt?

56cd575c5c77eddd.png

Schritt 2: Die richtige Seite verwenden

Um solche Fehler zu vermeiden, ist es wichtig, immer einen touchAnchorId und einen touchAnchorSide auszuwählen, die während der gesamten Animation immer in eine Richtung verlaufen.

In dieser Animation bewegen sich sowohl die right-Seite als auch die left-Seite des Mondes in eine Richtung über den Bildschirm.

Sowohl bottom als auch top kehren jedoch die Richtung um. Wenn OnSwipe versucht, sie zu verfolgen, wird es verwirrt, wenn sich ihre Richtung ändert.

  1. Damit die Animation auf Berührungsereignisse reagiert, ändern Sie touchAnchorSide in right.

step7.xml

<!-- Fix OnSwipe by changing touchAnchorSide →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="right"
/>

Schritt 3: dragDirection verwenden

Sie können dragDirection auch mit touchAnchorSide kombinieren, um einen Seitenpfad in eine andere Richtung zu lenken als normalerweise. Es ist weiterhin wichtig, dass sich die touchAnchorSide nur in eine Richtung bewegt. Sie können MotionLayout jedoch angeben, in welcher Richtung die Bewegung erfasst werden soll. Sie können beispielsweise touchAnchorSide="bottom" beibehalten, aber dragDirection="dragRight" hinzufügen. Dadurch wird die Position des unteren Rands der Ansicht von MotionLayout verfolgt, aber die Position wird nur berücksichtigt, wenn sich der untere Rand nach rechts bewegt (vertikale Bewegungen werden ignoriert). Auch wenn sich der untere Teil auf und ab bewegt, wird er mit OnSwipe korrekt animiert.

  1. Aktualisiere OnSwipe, damit die Bewegung des Mondes richtig erfasst wird.

step7.xml

<!-- Using dragDirection to control the direction of drag tracking →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
       motion:dragDirection="dragRight"
/>

Jetzt ausprobieren

  1. Führen Sie die App noch einmal aus und versuchen Sie, den Mond über den gesamten Pfad zu ziehen. Auch wenn die Animation einen komplexen Bogen beschreibt, kann MotionLayout sie als Reaktion auf Wischereignisse fortsetzen.

5458dff382261427.gif

11. Bewegung mit Code ausführen

Mit MotionLayout lassen sich in Kombination mit CoordinatorLayout ansprechende Animationen erstellen. In diesem Schritt erstellen Sie mit MotionLayout einen minimierbaren Header.

Schritt 1: Vorhandenen Code ansehen

  1. Öffnen Sie layout/activity_step8.xml, um zu beginnen.
  2. In layout/activity_step8.xml sehen Sie, dass bereits ein funktionierendes CoordinatorLayout und AppBarLayout erstellt wurde.

activity_step8.xml

<androidx.coordinatorlayout.widget.CoordinatorLayout
       ...>
   <com.google.android.material.appbar.AppBarLayout
           android:id="@+id/appbar_layout"
           android:layout_width="match_parent"
           android:layout_height="180dp">
       <androidx.constraintlayout.motion.widget.MotionLayout
               android:id="@+id/motion_layout"
               ... >
           ...
       </androidx.constraintlayout.motion.widget.MotionLayout>
   </com.google.android.material.appbar.AppBarLayout>
  
   <androidx.core.widget.NestedScrollView
           ...
           motion:layout_behavior="@string/appbar_scrolling_view_behavior" >
           ...
   </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

In diesem Layout wird ein CoordinatorLayout verwendet, um Scrollinformationen zwischen dem NestedScrollView und dem AppBarLayout zu teilen. Wenn also NestedScrollView nach oben gescrollt wird, wird AppBarLayout über die Änderung informiert. So implementieren Sie eine zusammenklappbare Symbolleiste wie diese unter Android: Das Scrollen des Texts wird mit dem zusammenklappbaren Header koordiniert.

Die Bewegungsszene, auf die @id/motion_layout verweist, ähnelt der Bewegungsszene im letzten Schritt. Die OnSwipe-Deklaration wurde jedoch entfernt, damit sie mit CoordinatorLayout funktioniert.

  1. Führen Sie die App aus und fahren Sie mit Schritt 8 fort. Wenn Sie den Text scrollen, sehen Sie, dass sich der Mond nicht bewegt.

Schritt 2: MotionLayout scrollbar machen

  1. Damit die MotionLayout-Ansicht scrollt, sobald die NestedScrollView-Ansicht scrollt, fügen Sie motion:minHeight und motion:layout_scrollFlags zu MotionLayout hinzu.

activity_step8.xml

<!-- Add minHeight and layout_scrollFlags to the MotionLayout -->

<androidx.constraintlayout.motion.widget.MotionLayout
       android:id="@+id/motion_layout"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       motion:layoutDescription="@xml/step8"
       motion:motionDebug="SHOW_PATH"
       android:minHeight="80dp"
       motion:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"  >
  1. Führen Sie die App noch einmal aus und fahren Sie mit Schritt 8 fort. Sie sehen, dass der MotionLayout beim Scrollen nach oben minimiert wird. Die Animation wird jedoch noch nicht auf Grundlage des Scrollverhaltens ausgeführt.

Schritt 3: Bewegung mit Code verschieben

  1. Öffnen Sie Step8Activity.kt . Bearbeiten Sie die Funktion coordinateMotion(), um MotionLayout über die Änderungen der Scrollposition zu informieren.

Step8Activity.kt

// TODO: set progress of MotionLayout based on an AppBarLayout.OnOffsetChangedListener

private fun coordinateMotion() {
    val appBarLayout: AppBarLayout = findViewById(R.id.appbar_layout)
    val motionLayout: MotionLayout = findViewById(R.id.motion_layout)

    val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
        val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
        motionLayout.progress = seekPosition
    }

    appBarLayout.addOnOffsetChangedListener(listener)
}

Mit diesem Code wird ein OnOffsetChangedListener registriert, das jedes Mal aufgerufen wird, wenn der Nutzer mit dem aktuellen Scroll-Offset scrollt.

MotionLayout unterstützt das Suchen nach dem Übergang durch Festlegen der Eigenschaft „progress“. Wenn Sie zwischen einem verticalOffset und einem prozentualen Fortschritt umrechnen möchten, teilen Sie durch den gesamten Scrollbereich.

Jetzt ausprobieren

  1. Stellen Sie die App noch einmal bereit und führen Sie die Animation aus Schritt 8 aus. Sie sehen, dass MotionLayout die Animation basierend auf der Scrollposition fortsetzt.

ee5ce4d9e33a59ca.gif

Mit MotionLayout lassen sich benutzerdefinierte dynamische Animationen für minimierbare Symbolleisten erstellen. Mit einer Folge von KeyFrames können Sie sehr auffällige Effekte erzielen.

12. Glückwunsch

In diesem Codelab wurde die grundlegende API von MotionLayout behandelt.

Weitere Beispiele für MotionLayout finden Sie im offiziellen Beispiel. Dokumentation

Weitere Informationen

MotionLayout unterstützt noch mehr Funktionen, die in diesem Codelab nicht behandelt werden, z. B. KeyCycle,, mit dem Sie Pfade oder Attribute mit sich wiederholenden Zyklen steuern können, und KeyTimeCycle,, mit dem Sie Animationen basierend auf der Uhrzeit erstellen können. In den Beispielen finden Sie Beispiele für die einzelnen Typen.

Links zu anderen Codelabs in diesem Kurs finden Sie auf der Landingpage für die Codelabs zum Thema „Erweitertes Android in Kotlin“.