Enquanto seu app está sendo usado, novas informações aparecem na tela e as informações antigas informações sejam removidas. Mudar imediatamente o que é mostrado na tela pode ser desagradável e os usuários podem perder conteúdos novos que aparecem repentinamente. As animações estão lentas as mudanças e chamar a atenção do usuário com movimento, para que as atualizações sejam mais óbvias.
Existem três animações comuns que você pode usar para mostrar ou ocultar uma visualização: revelar animações de crossfade e animação de cardflip.
Criar uma animação de fading cruzado
Uma animação de crossfade, também conhecida como dissolver, esmaece gradualmente.
um View
ou
ViewGroup
ao mesmo tempo
desaparecer em outra. Essa animação é útil para situações em que você deseja
alternar conteúdo ou visualizações no app. A animação de crossfade mostrada aqui usa
ViewPropertyAnimator
,
que está disponível para o Android 3.1 (nível 12 da API) e versões mais recentes.
Veja um exemplo de crossfade de um indicador de progresso para conteúdo de texto:
Criar as visualizações
Crie as duas visualizações em que você quer fazer a transição de crossfade. O exemplo a seguir cria uma indicador de progresso e uma visualização de texto rolável:
<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>
Configurar a animação de fading cruzado
Para configurar a animação de crossfade, faça o seguinte:
- Crie variáveis de membro para as visualizações que você quer usar na animação. Você precisa essas referências mais tarde, ao modificar as visualizações durante a animação.
- Definir a visibilidade da visualização que será mostrada
GONE
Isso impede que a visualização de usar o espaço de layout e os omite dos cálculos de layout, o que acelera processamento superior - Armazenar a
config_shortAnimTime
em cache propriedade do sistema em uma variável de membro. Essa propriedade define um "short" padrão da animação. Essa duração é ideal para animações sutis ou animações que ocorrem com frequência.config_longAnimTime
econfig_mediumAnimTime
também estão disponíveis.
Aqui está um exemplo que usa o layout do snippet de código anterior como o visualização do conteúdo da atividade:
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); } ... }
Fading cruzado nas visualizações
Quando as visualizações estiverem configuradas corretamente, faça o fading cruzado da seguinte forma:
- Para a visualização que será mostrada, defina o valor Alfa como 0 e a visibilidade
para
VISIBLE
do início deGONE
. Isso torna a visualização visível, mas transparente. - Para a visualização que será mostrada, anime o valor Alfa de 0 a 1. Para o que desaparece, anime o valor Alfa de 1 a 0.
- Usando
onAnimationEnd()
em umAnimator.AnimatorListener
, defina a visibilidade da visualização que está desaparecendo comoGONE
. Embora o valor Alfa for 0. Definir a visibilidade da visualização comoGONE
impede que ela seja de usar o espaço de layout e os omite dos cálculos de layout, o que acelera processamento.
O método a seguir mostra um exemplo de como fazer isso:
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); } }); } }
Criar uma animação de virada de cartão
A virada de cartão alterna entre as visualizações do conteúdo, mostrando uma animação que emula
virando um cartão. A animação de virada de cartão mostrada aqui usa
FragmentTransaction
Veja a aparência de uma virada de cartão:
Criar os objetos animadores
Para criar a animação de virada de cartão, são necessários quatro animadores. Dois animadores são para quando a frente do cartão é animada para fora, para a esquerda e quando ela é animada. e da esquerda para a direita. Os outros dois animadores servem para quando a parte de trás do cartão é animado para dentro e para a direita e quando é animado para a direita.
<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>
<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>
<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>
<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>
Criar as visualizações
Cada lado do card é um layout separado que pode incluir qualquer conteúdo que você querem, como duas visualizações de texto, duas imagens ou qualquer combinação de visualizações para virar entre eles. Use os dois layouts nos fragmentos que serão animados posteriormente. A o layout abaixo cria um lado de um cartão, que mostra 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>
O próximo layout cria o outro lado do cartão, que exibe uma
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" />
Criar os fragmentos
Crie classes de fragmento para a frente e para o verso do cartão. No seu fragmento
, retornam os layouts que você criou a partir do
onCreateView()
. Depois, você pode criar instâncias desse fragmento na atividade mãe
em que você quer mostrar o card.
O exemplo a seguir mostra classes de fragmentos aninhadas na atividade pai que os utiliza:
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); } } }
Animar a virada de cartão
Mostre os fragmentos dentro de uma atividade mãe. Para fazer isso, crie o layout
para sua atividade. O exemplo a seguir cria uma
FrameLayout
que você pode adicionar
fragmentos no tempo de execução:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
No código da atividade, defina a visualização de conteúdo como o layout que você criar. É recomendável mostrar um fragmento padrão quando a atividade for criada. A a seguinte atividade de exemplo mostra como exibir a frente do cartão pela padrão:
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(); } } ... }
Com a parte da frente do cartão visível, você pode mostrar o verso com a animação de virar a tela no momento adequado. Crie um método para mostrar o outro lado o card que faz o seguinte:
- Define as animações personalizadas que você criou para as transições de fragmento.
- Substitui o fragmento exibido por um novo e anima esse evento. com as animações personalizadas que você criou.
- Adiciona o fragmento exibido anteriormente à backstack do fragmento. Assim, quando o usuário toca no botão "Voltar", o cartão vira de volta.
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(); } }
Criar uma animação de revelação circular
Revelar animações fornece continuidade visual aos usuários ao mostrar ou ocultar um grupo
dos elementos da interface. A
ViewAnimationUtils.createCircularReveal()
permite animar um círculo de recorte para revelar ou ocultar uma visualização. Isso
animação é fornecida
ViewAnimationUtils
,
que está disponível para o Android 5.0 (nível 21 da API) e versões mais recentes.
Veja um exemplo de como revelar uma visualização anteriormente invisível:
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); }
A animação ViewAnimationUtils.createCircularReveal()
leva cinco parâmetros.
O primeiro parâmetro é a visualização que você quer ocultar ou mostrar na tela. A
os dois parâmetros seguintes são as coordenadas X e Y para o centro do recorte
círculo Normalmente, é o centro da visualização, mas você também pode usar o
em que o usuário toca para que a animação comece onde ele selecionou. A
o quarto parâmetro é o raio inicial do círculo de recorte.
No exemplo anterior, o raio inicial é definido como zero para que a visualização que está sendo exibido fica oculto pelo círculo. O último parâmetro é o raio final, do círculo. Ao exibir uma visualização, aumente o raio final do que para que ela seja totalmente revelada antes do término da animação.
Para ocultar uma visualização que estava visível, faça o seguinte:
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); }
Nesse caso, o raio inicial do círculo de recorte é definido como o maior
a visualização para que ela fique visível antes do início da animação. O resultado
ray é definido como zero para que a visualização fique oculta quando a animação termina.
Adicione um listener à animação para que a visibilidade da visualização possa ser definida como
INVISIBLE
quando a animação
é concluída.