Mientras se usa la app, aparece información nueva en la pantalla y la anterior y cómo se quita información. Cambiar de inmediato lo que se muestra en la pantalla y es posible que los usuarios se pierdan el contenido nuevo que aparece de repente. Animaciones lentas los cambios y llamar la atención del usuario con movimiento para que las actualizaciones más evidentes.
Hay tres animaciones comunes que puedes usar para mostrar u ocultar una vista: revelar animaciones de encadenado y de giro de tarjetas.
Cómo crear una animación de encadenado
Una animación de encadenado, también conocida como disolver, se desvanece gradualmente
un View
o
ViewGroup
en simultáneo
atenuándose en otro. Esta animación es útil para situaciones en las que quieres
cambiar el contenido o las vistas en tu app La animación de encadenado que se muestra aquí usa
ViewPropertyAnimator
:
que está disponible para Android 3.1 (nivel de API 12) y versiones posteriores.
El siguiente es un ejemplo de un encadenado entre un indicador de progreso y contenido de texto:
Cómo crear las vistas
Crea las dos vistas que deseas encadenar. En el siguiente ejemplo, se crea un un indicador de progreso y una vista de texto desplazable:
<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>
Cómo configurar la animación de encadenado
Para configurar la animación de encadenado, haz lo siguiente:
- Crea variables de miembros para las vistas que quieres encadenar. Necesitas estas referencias más adelante cuando modifiques las vistas durante la animación.
- Establecer la visibilidad de la vista del fundido de entrada
GONE
Esto evita que la vista de usar el espacio de diseño y lo omite de los cálculos de diseño, lo que acelera procesamiento en línea - Almacena en caché el
config_shortAnimTime
system en una variable de miembros. Esta propiedad define un valor estándar "short" de la animación. Esta duración es ideal para animaciones sutiles o animaciones que ocurren con frecuencia.config_longAnimTime
yconfig_mediumAnimTime
también están disponibles.
A continuación, te mostramos un ejemplo en el que se usa el diseño del fragmento de código anterior como vista del contenido de la actividad:
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); } ... }
Cómo encadenar las vistas
Una vez que las vistas estén configuradas correctamente, haz lo siguiente para encadenarlas:
- Para la vista del fundido de entrada, establece el valor alfa en 0 y la visibilidad.
a
VISIBLE
desde la parámetro de configuración deGONE
. Esto hace que la vista sea visible, pero transparente. - Para la vista del fundido de entrada, anima su valor Alfa de 0 a 1. Para el que se desvanece, anima el valor alfa de 1 a 0.
- Usando
onAnimationEnd()
en unaAnimator.AnimatorListener
, establece la visibilidad de la vista que se desvanece enGONE
. Si bien el valor alfa es 0, si se establece la visibilidad de la vista enGONE
, se impide la visualización de usar el espacio de diseño y lo omite de los cálculos de diseño, lo que acelera el procesamiento.
En el siguiente método, se muestra un ejemplo de cómo lograrlo:
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); } }); } }
Cómo crear una animación de giro de tarjetas
Los giros de tarjetas cambian entre vistas de contenido mostrando una animación que se emula
una tarjeta volteando. En la animación de giro de tarjetas que se muestra aquí, se usa
FragmentTransaction
Una animación de giro de tarjetas se ve de la siguiente manera:
Crea los objetos animadores
Para crear la animación de giro de tarjetas, necesitas cuatro animadores. Dos animadores para cuando se anima el frente de la tarjeta y hacia la izquierda, y cuando se anima desde y hacia la izquierda. Los otros dos animadores sirven para cuando el reverso de la tarjeta en la parte derecha y desde la derecha, y cuando se anima hacia afuera y hacia la derecha.
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>
Cómo crear las vistas
Cada lado de la tarjeta es un diseño independiente que puede incluir todo el contenido como dos vistas de texto, dos imágenes o cualquier combinación de vistas para voltear, en el medio. Usa los dos diseños en los fragmentos que animarás más adelante. El siguiente diseño crea un lado de una tarjeta, que muestra texto:
<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>
Y el siguiente diseño crea el otro lado de la tarjeta, que muestra una
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" />
Cómo crear los fragmentos
Crea clases de fragmentos para el anverso y el reverso de la tarjeta. En tu fragmento
de muestra, muestra los diseños que creaste a partir de la
onCreateView()
. Luego, puedes crear instancias de este fragmento en la actividad superior
donde quieres que aparezca la tarjeta.
En el siguiente ejemplo, se muestran clases de fragmentos anidados dentro de la actividad superior que los usa:
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); } } }
Cómo animar el giro de tarjetas
Muestra los fragmentos dentro de una actividad superior. Para ello, crea el diseño
para tu actividad. En el siguiente ejemplo, se crea un
FrameLayout
que puedes agregar
fragmentos en el tiempo de ejecución:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
En el código de la actividad, establece la vista de contenido para que sea el diseño que crearás. Se recomienda mostrar un fragmento predeterminado cuando se crea la actividad. El siguiente actividad de ejemplo muestra cómo mostrar el frente de la tarjeta al Predeterminado:
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(); } } ... }
Como se ve el frente de la tarjeta, puedes mostrar el reverso de la tarjeta con el animación de giro en un momento adecuado. Crea un método para mostrar el otro lado de la tarjeta que hace lo siguiente:
- Establece las animaciones personalizadas que creaste para las transiciones de fragmentos.
- Reemplaza el fragmento que se muestra con un fragmento nuevo y anima este evento con las animaciones personalizadas que creaste.
- Agrega el fragmento que se mostraba a la pila de actividades del fragmento; por lo tanto, cuando el usuario toca el botón Atrás, la tarjeta gira.
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(); } }
Cómo crear una animación de revelar circular
Las animaciones del efecto revelar proporcionan a los usuarios una continuidad visual cuando ocultas o muestras un grupo
de elementos de la IU. El
ViewAnimationUtils.createCircularReveal()
te permite animar un círculo de recorte para ocultar o mostrar una vista. Esta
se proporciona una animación
clase ViewAnimationUtils
,
que está disponible para Android 5.0 (nivel de API 21) y versiones posteriores.
En el siguiente ejemplo, podrás ver cómo mostrar una vista que anteriormente era invisible:
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); }
La animación ViewAnimationUtils.createCircularReveal()
toma cinco parámetros.
El primer parámetro es la vista que deseas ocultar o mostrar en la pantalla. El
los dos parámetros siguientes son las coordenadas X e Y para el centro de recorte
círculo. Normalmente, este es el centro de la vista, pero también puedes usar la
punto que el usuario presiona para que la animación comience donde seleccione. El
El cuarto parámetro es el radio de inicio del círculo de recorte.
En el ejemplo anterior, el radio inicial se estableció en cero para que la vista que se muestra está oculto por el círculo. El último parámetro es el radio final del círculo. Cuando muestres una vista, haz que el radio final sea mayor que el para que esta se revele por completo antes de que termine la animación.
Para ocultar una vista que anteriormente estaba visible, haz lo siguiente:
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); }
En este caso, el radio inicial del círculo de recorte es tan grande como
la vista para que sea visible antes de que comience la animación. La final
se establece en cero para que la vista se oculte cuando finalice la animación.
Agrega un objeto de escucha a la animación para establecer la visibilidad de la vista en
INVISIBLE
cuando la animación
de datos completados.