Podczas używania aplikacji na ekranie pojawiają się nowe informacje, a stare i stare zostały usunięte. Natychmiastowa zmiana tego, co pokazuje się na ekranie, przykuwają uwagę, przez co użytkownicy mogą przegapić nowe treści, które pojawiają się nagle. Animacje są wolne Porównuje zmiany i ruch, aby przyciągnąć uwagę użytkownika jest bardziej oczywiste.
Istnieją 3 popularne animacje, których można użyć do pokazania lub ukrycia widoku: ujawnianie animacje, animacje przenikania i cardflip.
Utwórz animację przenikania
Animacja przenikająca (nazywana też przenikaniem) stopniowo zanika
jeden View
lub
ViewGroup
jednocześnie
znikają z innego. Ta animacja jest przydatna, gdy chcesz:
przełączać treści lub widoki w aplikacji. Pokazana tutaj animacja przenikania korzysta z:
ViewPropertyAnimator
który jest dostępny na Androidzie 3.1 (poziom interfejsu API 12) i nowszym.
Oto przykład przejścia ze wskaźnika postępu na treść tekstową:
Tworzenie widoków
Utwórz dwa widoki, które będą zanikać. Ten przykład pozwala utworzyć wskaźnik postępu i przewijany widok tekstowy:
<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>
Konfigurowanie przenikania animacji
Aby skonfigurować animację przenikania, wykonaj te czynności:
- Utwórz zmienne składowe dla widoków, które chcesz przenikać. Potrzebujesz te odwołania później podczas modyfikowania widoków w trakcie animacji.
- Ustaw widoczność zaciemnianego widoku na
GONE
Spowoduje to zablokowanie wyświetlania nie korzysta z przestrzeni układu, co pomija go w obliczeniach układu, co przyspiesza przetwarzanie w górę - Pamięć podręczna
config_shortAnimTime
do właściwości systemowej. Ta właściwość definiuje standardowy „krótki” czas trwania animacji. Ten czas trwania jest odpowiedni w przypadku subtelnych animacji lub które często się pojawiają.config_longAnimTime
orazconfig_mediumAnimTime
.
Oto przykład, w którym wykorzystano układ z poprzedniego fragmentu kodu jako widok treści aktywności:
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); } ... }
Przechodzenie między widokami
Gdy widoki są skonfigurowane prawidłowo, możesz je przełączać w ten sposób:
- Dla widoku, który się zanika, ustaw wartość alfa na 0 i widoczność
do
VISIBLE
od początkowego poziomu ustawienieGONE
. Dzięki temu widok jest widoczny, ale przezroczysty. - Dla widoku, który zanika, animuj jego wartość alfa od 0 do 1. W przypadku atrybutu zanikający widok, należy animować wartość alfa od 1 do 0.
- Zastosowanie
onAnimationEnd()
wAnimator.AnimatorListener
, ustaw widoczność zanikającego widoku naGONE
. Mimo że wartość alfa wynosi 0, ustawienie widoczności widoku naGONE
uniemożliwia jego wyświetlenie nie korzysta z przestrzeni układu, co pomija go w obliczeniach układu, co przyspiesza w trakcie przetwarzania.
Oto przykład:
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); } }); } }
Utwórz animację odwrócenia karty
Odwrócenia karty przełączają się między widokami treści, pokazując animację, która emuluje
odwraca się kartę. Animacja odwrócenia karty wykorzystuje
FragmentTransaction
Tak wygląda odwrócenie karty:
Tworzenie obiektów animacji
Aby utworzyć animację odwrócenia karty, potrzebujesz 4 animatorów. Dwóch animatorów gdy przód karty przesuwa się na zewnątrz i w lewo oraz gdy jest animowana. do lewej i od lewej. Pozostałe dwa animacje są używane, gdy tył karty pojawia się w ruchu i w prawo oraz w prawo i na zewnątrz.
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>
Tworzenie widoków
Każda strona karty to oddzielny układ, który może zawierać dowolne treści, , np. dwa widoki tekstu, dwa obrazy lub dowolną kombinację widoków między nimi. Korzystaj z dwóch układów we fragmentach, które będziesz później animować. ten układ tworzy jedną stronę karty z tekstem:
<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>
Kolejny układ tworzy drugą stronę karty,
ImageView
:
<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" />
Tworzenie fragmentów
Utwórz klasy fragmentów dla przedniej i tylnej strony karty. We fragmencie
klas, zwraca układy utworzone przez
onCreateView()
. Można następnie utworzyć wystąpienia tego fragmentu w aktywności nadrzędnej
przy których chcesz ją wyświetlić.
Przykład poniżej pokazuje zagnieżdżone klasy fragmentów w aktywności nadrzędnej który z nich korzysta:
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); } } }
Animuj odwrócenie karty
Wyświetl fragmenty w aktywności nadrzędnej. Aby to zrobić, utwórz układ
za Twoją aktywność. Ten przykład pozwala utworzyć
FrameLayout
, które możesz dodać
fragmentami do pliku w czasie działania:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
W kodzie aktywności ustaw widok treści na utworzony przez siebie układ. Warto pokazywać domyślny fragment podczas tworzenia aktywności. Z przykładu poniżej dowiesz się, jak wyświetlić przód karty według: domyślnie:
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(); } } ... }
Gdy przód karty jest widoczny, możesz pokazać jej tylną część oraz w odpowiednim momencie odwrócić animację. Utwórz metodę pokazania drugiej strony dokumentu która umożliwia:
- Ustawia niestandardowe animacje utworzone dla przejść fragmentów.
- Zastępuje wyświetlany fragment nowym fragmentem i animuje to zdarzenie z Twoimi niestandardowymi animacjami.
- Dodaje poprzednio wyświetlany fragment do stosu fragmentów z tyłu, więc po użytkownik klika przycisk Wstecz, a karta odwraca się z powrotem.
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(); } }
Tworzenie animacji wyświetlania kołowego
Pokazuj animacje, które zapewniają użytkownikom ciągłość wizualną po wyświetleniu lub ukryciu grupy.
elementów interfejsu.
ViewAnimationUtils.createCircularReveal()
umożliwia animowanie okręgu przycinania w celu odsłonięcia lub ukrycia widoku. Ten
animacja jest udostępniana w
ViewAnimationUtils
zajęcia,
który jest dostępny na Androidzie 5.0 (poziom interfejsu API 21) i nowszym.
Oto przykład, jak pokazać, jak wyświetlić wcześniej niewidoczny widok:
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); }
Animacja ViewAnimationUtils.createCircularReveal()
ma 5 parametrów.
Pierwszy parametr to widok, który chcesz ukryć lub pokazać na ekranie.
dwa następne parametry to współrzędne X i Y środka klipu.
kręgu znajomych. Zazwyczaj jest to środek widoku, ale możesz też użyć
i klikaj ją, aby animacja zaczynała się w wybranym miejscu.
czwarty parametr jest promieniem początkowym okręgu przycinającego.
W poprzednim przykładzie początkowy promień jest ustawiony na zero, aby widok jest zasłaniana przez okrąg. Ostatni parametr to końcowy promień jego okręgu. Podczas wyświetlania widoku ustaw końcowy promień większy niż co pozwala wyświetlić pełny widok jeszcze przed zakończeniem animacji.
Aby ukryć widok widoczny wcześniej, wykonaj te czynności:
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); }
W tym przypadku początkowy promień okręgu przycinania jest ustawiony na taki
aby widok był widoczny przed rozpoczęciem animacji. Finał
promień jest ustawiony na zero, by po zakończeniu animacji widok był ukryty.
Dodaj do animacji detektor, aby widoczność widoku mogła być ustawiona na
INVISIBLE
, gdy animacja
.