Während Ihre App verwendet wird, werden neue Informationen auf dem Bildschirm angezeigt und alte Informationen entfernt. Wenn sich die Inhalte auf dem Bildschirm sofort ändern, kann das irritierend sein. Nutzer übersehen möglicherweise neue Inhalte, die plötzlich angezeigt werden. Animationen verlangsamen die Änderungen und lenken den Blick des Nutzers durch Bewegung auf die Updates, sodass sie deutlicher zu erkennen sind.
Es gibt drei gängige Animationen, mit denen Sie eine Ansicht ein- oder ausblenden können: Reveal-Animationen, Überblendungsanimationen und Cardflip-Animationen.
Überblendung erstellen
Bei einer Überblendungsanimation, auch Auflösung genannt, wird ein View oder ViewGroup allmählich ausgeblendet, während gleichzeitig ein anderes eingeblendet wird. Diese Animation ist nützlich, wenn Sie Inhalte oder Ansichten in Ihrer App wechseln möchten. Die hier gezeigte Überblendungsanimation verwendet ViewPropertyAnimator, das für Android 3.1 (API-Level 12) und höher verfügbar ist.
Hier ist ein Beispiel für eine Überblendung von einer Fortschrittsanzeige zu Textinhalten:
Ansichten erstellen
Erstellen Sie die beiden Ansichten, die Sie überblenden möchten. Im folgenden Beispiel werden eine Fortschrittsanzeige und eine scrollbare Textansicht erstellt:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView style="?android:textAppearanceMedium"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lorem_ipsum"
android:padding="16dp" />
</ScrollView>
<ProgressBar android:id="@+id/loading_spinner"
style="?android:progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
Überblendungsanimation einrichten
So richten Sie die Überblendungsanimation ein:
- Erstellen Sie Mitgliedsvariablen für die Ansichten, die Sie überblenden möchten. Sie benötigen diese Referenzen später, wenn Sie die Ansichten während der Animation ändern.
- Legen Sie die Sichtbarkeit der einzublendenden Ansicht auf
GONEfest. Dadurch wird verhindert, dass die Ansicht Layout-Platz verwendet, und sie wird aus Layout-Berechnungen ausgeschlossen, was die Verarbeitung beschleunigt. - Speichern Sie die Systemproperty
config_shortAnimTimein einer Mitgliedsvariablen im Cache. Mit dieser Eigenschaft wird eine Standarddauer für die Animation definiert, die als „kurz“ gilt. Diese Dauer ist ideal für subtile oder häufige Animationen.config_longAnimTimeundconfig_mediumAnimTimesind ebenfalls verfügbar.
Hier ist ein Beispiel, in dem das Layout aus dem vorherigen Code-Snippet als Ansicht für den Aktivitätsinhalt verwendet wird:
Kotlin
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_crossfade) contentView = findViewById(R.id.content) loadingView = findViewById(R.id.loading_spinner) // Initially hide the content view. contentView.visibility = View.GONE // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime) } ... }
Java
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_crossfade); contentView = findViewById(R.id.content); loadingView = findViewById(R.id.loading_spinner); // Initially hide the content view. contentView.setVisibility(View.GONE); // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = getResources().getInteger( android.R.integer.config_shortAnimTime); } ... }
Ansichten überblenden
Wenn die Ansichten richtig eingerichtet sind, können Sie sie so überblenden:
- Legen Sie für die Ansicht, die eingeblendet wird, den Alphawert auf 0 und die Sichtbarkeit auf
VISIBLEfest. Die ursprüngliche Einstellung istGONE. Dadurch wird die Ansicht sichtbar, aber transparent. - Animieren Sie für die Ansicht, die eingeblendet wird, den Alphawert von 0 auf 1. Animieren Sie für die Ansicht, die ausgeblendet wird, den Alphawert von 1 auf 0.
- Wenn Sie
onAnimationEnd()in einemAnimator.AnimatorListenerverwenden, legen Sie die Sichtbarkeit der Ansicht, die ausgeblendet wird, aufGONEfest. Auch wenn der Alphawert 0 ist, wird durch Festlegen der Sichtbarkeit der Ansicht aufGONEverhindert, dass die Ansicht Layout-Platz verwendet und in Layoutberechnungen berücksichtigt wird. Dadurch wird die Verarbeitung beschleunigt.
Die folgende Methode zeigt ein Beispiel dafür:
Kotlin
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... private fun crossfade() { contentView.apply { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. alpha = 0f visibility = View.VISIBLE // Animate the content view to 100% opacity and clear any animation // listener set on the view. animate() .alpha(1f) .setDuration(shortAnimationDuration.toLong()) .setListener(null) } // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration.toLong()) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { loadingView.visibility = View.GONE } }) } }
Java
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... private void crossfade() { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. contentView.setAlpha(0f); contentView.setVisibility(View.VISIBLE); // Animate the content view to 100% opacity and clear any animation // listener set on the view. contentView.animate() .alpha(1f) .setDuration(shortAnimationDuration) .setListener(null); // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { loadingView.setVisibility(View.GONE); } }); } }
Karten-Flip-Animation erstellen
Beim Umblättern von Karten wird zwischen Ansichten von Inhalten gewechselt. Dabei wird eine Animation angezeigt, die das Umblättern einer Karte simuliert. Für die hier gezeigte Animation zum Umdrehen der Karte wird FragmentTransaction verwendet.
So sieht das Umdrehen einer Karte aus:
Animator-Objekte erstellen
Für die Animation des Kartenumdrehens benötigen Sie vier Animators. Zwei Animators sind für den Fall, dass die Vorderseite der Karte nach links animiert wird und wenn sie von links animiert wird. Die anderen beiden Animator-Objekte sind für das Einblenden der Rückseite der Karte von rechts und das Ausblenden der Karte nach rechts zuständig.
card_flip_left_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" />
<!-- Rotate. -->
<objectAnimator
android:valueFrom="-180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
card_flip_left_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Rotate. -->
<objectAnimator
android:valueFrom="0"
android:valueTo="180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
card_flip_right_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" />
<!-- Rotate. -->
<objectAnimator
android:valueFrom="180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
card_flip_right_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Rotate. -->
<objectAnimator
android:valueFrom="0"
android:valueTo="-180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
Ansichten erstellen
Jede Seite der Karte ist ein separates Layout, das beliebige Inhalte enthalten kann, z. B. zwei Textansichten, zwei Bilder oder eine beliebige Kombination von Ansichten, zwischen denen gewechselt werden kann. Verwenden Sie die beiden Layouts in den Fragmenten, die Sie später animieren. Mit dem folgenden Layout wird eine Seite einer Karte erstellt, auf der Text angezeigt wird:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#a6c"
android:padding="16dp"
android:gravity="bottom">
<TextView android:id="@android:id/text1"
style="?android:textAppearanceLarge"
android:textStyle="bold"
android:textColor="#fff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_title" />
<TextView style="?android:textAppearanceSmall"
android:textAllCaps="true"
android:textColor="#80ffffff"
android:textStyle="bold"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_description" />
</LinearLayout>
Im nächsten Layout wird die andere Seite der Karte erstellt, auf der ein ImageView angezeigt wird:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/image1"
android:scaleType="centerCrop"
android:contentDescription="@string/description_image_1" />
Fragmente erstellen
Erstellen Sie Fragmentklassen für die Vorder- und Rückseite der Karte. Geben Sie in Ihren Fragmentklassen die von Ihnen erstellten Layouts über die Methode onCreateView() zurück. Anschließend können Sie Instanzen dieses Fragments in der übergeordneten Aktivität erstellen, in der die Karte angezeigt werden soll.
Im folgenden Beispiel werden verschachtelte Fragmentklassen in der übergeordneten Aktivität gezeigt, die sie verwendet:
Kotlin
class CardFlipActivity : FragmentActivity() { ... /** * A fragment representing the front of the card. */ class CardFrontFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_front, container, false) } /** * A fragment representing the back of the card. */ class CardBackFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_back, container, false) } }
Java
public class CardFlipActivity extends FragmentActivity { ... /** * A fragment representing the front of the card. */ public class CardFrontFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_front, container, false); } } /** * A fragment representing the back of the card. */ public class CardBackFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_back, container, false); } } }
Kartenumdrehung animieren
Fragmente in einer übergeordneten Aktivität anzeigen Erstellen Sie dazu das Layout für Ihre Aktivität. Im folgenden Beispiel wird ein FrameLayout erstellt, dem Sie zur Laufzeit Fragmente hinzufügen können:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Legen Sie im Aktivitätscode die Inhaltsansicht auf das von Ihnen erstellte Layout fest. Es empfiehlt sich, beim Erstellen der Aktivität ein Standardfragment anzuzeigen. Im folgenden Beispiel wird gezeigt, wie die Vorderseite der Karte standardmäßig angezeigt wird:
Kotlin
class CardFlipActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_activity_card_flip) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .add(R.id.container, CardFrontFragment()) .commit() } } ... }
Java
public class CardFlipActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_activity_card_flip); if (savedInstanceState == null) { getSupportFragmentManager() .beginTransaction() .add(R.id.container, new CardFrontFragment()) .commit(); } } ... }
Wenn die Vorderseite der Karte zu sehen ist, können Sie die Rückseite der Karte mit der Flip-Animation zu einem geeigneten Zeitpunkt anzeigen. Erstellen Sie eine Methode, mit der die andere Seite der Karte angezeigt wird und die Folgendes ausführt:
- Legt die benutzerdefinierten Animationen fest, die Sie für die Fragmentübergänge erstellt haben.
- Ersetzt das angezeigte Fragment durch ein neues Fragment und animiert dieses Ereignis mit den benutzerdefinierten Animationen, die Sie erstellt haben.
- Fügt das zuvor angezeigte Fragment dem Fragment-Backstack hinzu. Wenn der Nutzer also auf den Button „Zurück“ tippt, wird die Karte wieder umgedreht.
Kotlin
class CardFlipActivity : FragmentActivity() { ... private fun flipCard() { if (showingBack) { supportFragmentManager.popBackStack() return } // Flip to the back. showingBack = true // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. supportFragmentManager.beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is tapped. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out ) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, CardBackFragment()) // Add this transaction to the back stack, letting users press // the Back button to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit() } }
Java
public class CardFlipActivity extends FragmentActivity { ... private void flipCard() { if (showingBack) { getSupportFragmentManager().popBackStack(); return; } // Flip to the back. showingBack = true; // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. getSupportFragmentManager() .beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is pressed. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, new CardBackFragment()) // Add this transaction to the back stack, letting users press // Back to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit(); } }
Animation mit kreisförmiger Einblendung erstellen
Reveal-Animationen sorgen für visuelle Kontinuität, wenn Sie eine Gruppe von UI-Elementen ein- oder ausblenden. Mit der Methode ViewAnimationUtils.createCircularReveal() können Sie einen Zuschneidekreis animieren, um eine Ansicht ein- oder auszublenden. Diese Animation wird in der Klasse ViewAnimationUtils bereitgestellt, die ab Android 5.0 (API-Level 21) verfügbar ist.
Hier ein Beispiel dafür, wie Sie eine zuvor unsichtbare Ansicht einblenden:
Kotlin
// A previously invisible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the final radius for the clipping circle. val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animator for this view. The start radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius) // Make the view visible and start the animation. myView.visibility = View.VISIBLE anim.start() } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.visibility = View.INVISIBLE }
Java
// A previously invisible view. View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the final radius for the clipping circle. float finalRadius = (float) Math.hypot(cx, cy); // Create the animator for this view. The start radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius); // Make the view visible and start the animation. myView.setVisibility(View.VISIBLE); anim.start(); } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.setVisibility(View.INVISIBLE); }
Die ViewAnimationUtils.createCircularReveal()-Animation hat fünf Parameter.
Der erste Parameter ist die Ansicht, die auf dem Bildschirm ein- oder ausgeblendet werden soll. Die nächsten beiden Parameter sind die X- und Y-Koordinaten für den Mittelpunkt des Zuschneidekreises. Normalerweise ist dies die Mitte der Ansicht. Sie können aber auch den Punkt verwenden, den der Nutzer antippt, damit die Animation dort beginnt, wo er sie auswählt. Der vierte Parameter ist der Startradius des Zuschneidekreises.
Im vorherigen Beispiel ist der Anfangsradius auf null gesetzt, sodass die angezeigte Ansicht vom Kreis verdeckt wird. Der letzte Parameter ist der endgültige Radius des Kreises. Wenn Sie eine Ansicht einblenden, muss der endgültige Radius größer als die Ansicht sein, damit die Ansicht vollständig sichtbar ist, bevor die Animation abgeschlossen ist.
So blenden Sie eine zuvor sichtbare Ansicht aus:
Kotlin
// A previously visible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the initial radius for the clipping circle. val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animation. The final radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f) // Make the view invisible when the animation is done. anim.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) myView.visibility = View.INVISIBLE } }) // Start the animation. anim.start() } else { // Set the view to visible without a circular reveal animation below // Android 5.0. myView.visibility = View.VISIBLE }
Java
// A previously visible view. final View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the initial radius for the clipping circle. float initialRadius = (float) Math.hypot(cx, cy); // Create the animation. The final radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f); // Make the view invisible when the animation is done. anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); myView.setVisibility(View.INVISIBLE); } }); // Start the animation. anim.start(); } else { // Set the view to visible without a circular reveal animation below Android // 5.0. myView.setVisibility(View.VISIBLE); }
In diesem Fall wird der anfängliche Radius des Zuschneidekreises so groß wie die Ansicht festgelegt, damit die Ansicht vor Beginn der Animation sichtbar ist. Der endgültige Radius wird auf null gesetzt, damit die Ansicht ausgeblendet wird, wenn die Animation abgeschlossen ist.
Fügen Sie der Animation einen Listener hinzu, damit die Sichtbarkeit der Ansicht auf INVISIBLE gesetzt werden kann, wenn die Animation abgeschlossen ist.