Si se requiere voltear una vista como si fuese un juego de descubrir una carta, se puede hacer con la animación (Flip Card Animation)

Preparativos
primero debemos declarar las animaciones siguientes en el directorio res/animator
archivo flip_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:valueFrom="1.0"
android:valueTo="0.0" />
<objectAnimator
android:duration="750"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="rotationY"
android:repeatMode="reverse"
android:valueFrom="-180"
android:valueTo="0" />
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:startOffset="350"
android:valueFrom="0.0"
android:valueTo="1.0" />
</set>
archivo flip_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="750"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="rotationY"
android:valueFrom="0"
android:valueTo="180" />
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:startOffset="350"
android:valueFrom="1.0"
android:valueTo="0.0" />
</set>
Diseño de la vistas
Diseñar las caras de la carta, la cara de detrás y la delante
layout/card_front.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardBackgroundColor="@android:color/holo_green_dark"
app:cardCornerRadius="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="56dp"
android:layout_height="56dp"
app:srcCompat="@drawable/ic_baseline_android_24"
app:tint="@android:color/holo_green_light" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
archivo card_back.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardCornerRadius="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="56dp"
android:layout_height="56dp"
app:srcCompat="@drawable/ic_round_favorite_24"
app:tint="@android:color/holo_red_dark" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
Unir las dos caras en una vista
Ahora donde queremos mostrar la vista con las dos caras
<FrameLayout
android:id="@+id/card_flip"
android:layout_width="150dp"
android:layout_height="240dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="@+id/card_face_front"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="@layout/card_front"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout
android:id="@+id/card_face_back"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<include
layout="@layout/card_back"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</FrameLayout>
Animación Card Flip Animator
Con el método FlipCard podemos animar la vistas, especificando la cara visible y la cara oculta
private fun flipCard(context: Context, visibleView: View, inVisibleView: View) {
try {
visibleView.visibility = View.VISIBLE
val scale = context.resources.displayMetrics.density
val cameraDist = 8000F * scale
visibleView.cameraDistance = cameraDist
inVisibleView.cameraDistance = cameraDist
val flipOutAnimatorSet = AnimatorInflater.loadAnimator(context, R.animator.flip_out) as AnimatorSet
flipOutAnimatorSet.setTarget(inVisibleView)
val flipInAnimatorSet = AnimatorInflater.loadAnimator(context, R.animator.flip_in) as AnimatorSet
flipInAnimatorSet.setTarget(visibleView)
flipOutAnimatorSet.start()
flipInAnimatorSet.start()
flipInAnimatorSet.doOnEnd {
inVisibleView.visibility = View.GONE
binding.cardFlip.isEnabled = true
}
} catch (e: Exception) {
Log.e(TAG, "flipCard: $e")
}
}
Su uso
Si queremos que en cada toque encima de la carta se volteé, el falg isCardFront es para saber a que cara se encuentra y la it.isEnabled es para prevenir multiples toques mientras se ejecuta la animación
private var isCardFront = true
...
binding.cardFlip.setOnClickListener {
it.isEnabled = false
isCardFront = !isCardFront
if (isCardFront) {
flipCard(requireContext(), binding.cardFaceFront, binding.cardFaceBack)
} else {
flipCard(requireContext(), binding.cardFaceBack, binding.cardFaceFront)
}
}
Demostración
Dejo video demostrativo para ver su animación Flip Card Animation
Extra en Jetpack Compose
Si se quiere realizar con jetpack compose dejo enlace de la fuente: https://fvilarino.medium.com/creating-a-rotating-card-in-jetpack-compose-ba94c7dd76fb