Hola amigos 馃憢 Bienvenidos a un nuevo tutorial de Universo Android. Hoy aprenderemos a crear animaciones y transiciones en Jetpack Compose, el framework moderno de UI declarativa de Android que hace las animaciones m谩s simples y poderosas.
Al finalizar este tutorial tendr谩s una aplicaci贸n que incluir谩:
- Animaciones b谩sicas con animate*AsState
- Transiciones con AnimatedVisibility
- Animaciones de contenido con AnimatedContent
- Transiciones personalizadas
- Animaciones infinitas
- Gestos animados
- Ejemplos pr谩cticos
馃煩 1. ¿Qu茅 son las Transiciones en Compose?
Las transiciones en Compose son animaciones declarativas que responden a cambios de estado. Son m谩s simples que las animaciones tradicionales de Android View y se integran perfectamente con el paradigma reactivo.
馃煩 2. Tipos de Animaciones
- animate*AsState: Anima valores simples
- AnimatedVisibility: Mostrar/ocultar con animaci贸n
- AnimatedContent: Transiciones entre contenidos
- Crossfade: Fundido entre composables
- updateTransition: Transiciones complejas
馃煩 3. Crear el Proyecto
Creamos un nuevo proyecto en Android Studio con Empty Activity y seleccionamos Compose.
馃煩 4. Dependencias
馃搫 build.gradle (Module: app)
dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'androidx.activity:activity-compose:1.8.2'
// Compose BOM
implementation platform('androidx.compose:compose-bom:2023.10.01')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
// Animaciones
implementation 'androidx.compose.animation:animation'
}馃煩 5. MainActivity con Compose
馃搫 MainActivity.kt
package com.example.composetransitions
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.composetransitions.ui.theme.ComposeTransitionsTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeTransitionsTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
TransitionsApp()
}
}
}
}
}
@Composable
fun TransitionsApp() {
var selectedTab by remember { mutableStateOf(0) }
Column(modifier = Modifier.fillMaxSize()) {
TabRow(selectedTabIndex = selectedTab) {
Tab(
selected = selectedTab == 0,
onClick = { selectedTab = 0 },
text = { Text("B谩sicas") }
)
Tab(
selected = selectedTab == 1,
onClick = { selectedTab = 1 },
text = { Text("Visibilidad") }
)
Tab(
selected = selectedTab == 2,
onClick = { selectedTab = 2 },
text = { Text("Contenido") }
)
}
when (selectedTab) {
0 -> BasicAnimationsScreen()
1 -> VisibilityAnimationsScreen()
2 -> ContentAnimationsScreen()
}
}
}
@Preview(showBackground = true)
@Composable
fun PreviewTransitionsApp() {
ComposeTransitionsTheme {
TransitionsApp()
}
}馃煩 6. Animaciones B谩sicas
馃搫 BasicAnimationsScreen.kt
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
fun BasicAnimationsScreen() {
var expanded by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = "Animaciones B谩sicas",
style = MaterialTheme.typography.headlineMedium,
color = Color(0xFF2196F3)
)
// Animaci贸n de Tama帽o
AnimatedSizeBox(expanded)
// Animaci贸n de Color
AnimatedColorBox()
// Animaci贸n de Rotaci贸n
AnimatedRotationBox()
// Bot贸n para cambiar estado
Button(
onClick = { expanded = !expanded },
modifier = Modifier.fillMaxWidth()
) {
Text(if (expanded) "Contraer" else "Expandir")
}
}
}
@Composable
fun AnimatedSizeBox(expanded: Boolean) {
val size by animateDpAsState(
targetValue = if (expanded) 200.dp else 100.dp,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
),
label = "size"
)
Box(
modifier = Modifier
.size(size)
.background(Color(0xFF4CAF50), RoundedCornerShape(16.dp)),
contentAlignment = Alignment.Center
) {
Text("Tama帽o", color = Color.White)
}
}
@Composable
fun AnimatedColorBox() {
var colorState by remember { mutableStateOf(true) }
val color by animateColorAsState(
targetValue = if (colorState) Color(0xFF2196F3) else Color(0xFFFF5722),
animationSpec = tween(durationMillis = 1000),
label = "color"
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(80.dp)
.background(color, RoundedCornerShape(16.dp))
.clickable { colorState = !colorState },
contentAlignment = Alignment.Center
) {
Text("Toca para cambiar color", color = Color.White)
}
}
@Composable
fun AnimatedRotationBox() {
var rotated by remember { mutableStateOf(false) }
val rotation by animateFloatAsState(
targetValue = if (rotated) 360f else 0f,
animationSpec = tween(durationMillis = 1000),
label = "rotation"
)
Box(
modifier = Modifier
.size(100.dp)
.rotate(rotation)
.background(Color(0xFF9C27B0), RoundedCornerShape(16.dp))
.clickable { rotated = !rotated },
contentAlignment = Alignment.Center
) {
Text("Rotar", color = Color.White)
}
}馃煩 7. Animaciones de Visibilidad
馃搫 VisibilityAnimationsScreen.kt
import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
fun VisibilityAnimationsScreen() {
var visible1 by remember { mutableStateOf(false) }
var visible2 by remember { mutableStateOf(false) }
var visible3 by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = "Animaciones de Visibilidad",
style = MaterialTheme.typography.headlineMedium,
color = Color(0xFF2196F3)
)
// Fade In/Out
Button(onClick = { visible1 = !visible1 }) {
Text("Toggle Fade")
}
AnimatedVisibility(
visible = visible1,
enter = fadeIn(),
exit = fadeOut()
) {
MessageCard("Fade In/Out", Color(0xFF4CAF50))
}
// Slide In/Out
Button(onClick = { visible2 = !visible2 }) {
Text("Toggle Slide")
}
AnimatedVisibility(
visible = visible2,
enter = slideInVertically() + fadeIn(),
exit = slideOutVertically() + fadeOut()
) {
MessageCard("Slide Vertical", Color(0xFF2196F3))
}
// Expand/Collapse
Button(onClick = { visible3 = !visible3 }) {
Text("Toggle Expand")
}
AnimatedVisibility(
visible = visible3,
enter = expandVertically() + fadeIn(),
exit = shrinkVertically() + fadeOut()
) {
MessageCard("Expand/Collapse", Color(0xFFFF9800))
}
}
}
@Composable
fun MessageCard(text: String, color: Color) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = color),
shape = RoundedCornerShape(16.dp)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
contentAlignment = Alignment.Center
) {
Text(text, color = Color.White, style = MaterialTheme.typography.titleMedium)
}
}
}馃煩 8. Animaciones de Contenido
馃搫 ContentAnimationsScreen.kt
import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
@Composable
fun ContentAnimationsScreen() {
var count by remember { mutableStateOf(0) }
var selectedIcon by remember { mutableStateOf(0) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Animaciones de Contenido",
style = MaterialTheme.typography.headlineMedium,
color = Color(0xFF2196F3)
)
// AnimatedContent con contador
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = Color(0xFF4CAF50))
) {
Column(
modifier = Modifier.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Contador", color = Color.White)
AnimatedContent(
targetState = count,
transitionSpec = {
slideInVertically { it } + fadeIn() with
slideOutVertically { -it } + fadeOut()
},
label = "counter"
) { targetCount ->
Text(
text = "$targetCount",
style = MaterialTheme.typography.displayLarge,
color = Color.White
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Button(onClick = { count-- }) { Text("-") }
Button(onClick = { count++ }) { Text("+") }
Button(onClick = { count = 0 }) { Text("Reset") }
}
}
}
// Crossfade con iconos
val icons = listOf(
Icons.Default.Home,
Icons.Default.Favorite,
Icons.Default.Person,
Icons.Default.Settings
)
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = Color(0xFF2196F3))
) {
Column(
modifier = Modifier.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Crossfade Icons", color = Color.White)
Crossfade(
targetState = icons[selectedIcon],
label = "icon"
) { icon ->
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(80.dp),
tint = Color.White
)
}
Spacer(modifier = Modifier.height(16.dp))
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
icons.forEachIndexed { index, _ ->
Button(
onClick = { selectedIcon = index },
colors = ButtonDefaults.buttonColors(
containerColor = if (selectedIcon == index)
Color(0xFF1976D2) else Color.White
)
) {
Text("${index + 1}")
}
}
}
}
}
}
}馃煩 9. Animaci贸n Infinita
@Composable
fun InfiniteAnimationExample() {
val infiniteTransition = rememberInfiniteTransition(label = "infinite")
val scale by infiniteTransition.animateFloat(
initialValue = 1f,
targetValue = 1.5f,
animationSpec = infiniteRepeatable(
animation = tween(1000),
repeatMode = RepeatMode.Reverse
),
label = "scale"
)
Box(
modifier = Modifier
.size(100.dp)
.scale(scale)
.background(Color(0xFFFF5722), RoundedCornerShape(50.dp))
)
}馃煩 10. Especificaciones de Animaci贸n
// Tween (lineal con duraci贸n)
animationSpec = tween(durationMillis = 1000)
// Spring (efecto rebote)
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
// Keyframes (animaci贸n por cuadros)
animationSpec = keyframes {
durationMillis = 1000
0.5f at 500
0.8f at 800
}
// Repeatable (repetir animaci贸n)
animationSpec = repeatable(
iterations = 3,
animation = tween(1000),
repeatMode = RepeatMode.Reverse
)馃煩 11. Tabla de Funciones
| Funci贸n | Uso |
|---|---|
animateDpAsState | Anima Dp |
animateColorAsState | Anima Color |
animateFloatAsState | Anima Float |
AnimatedVisibility | Mostrar/ocultar |
AnimatedContent | Cambiar contenido |
Crossfade | Fundido cruzado |
updateTransition | Transiciones complejas |
馃煩 12. Efectos de Entrada/Salida
| Entrada | Salida |
|---|---|
fadeIn() | fadeOut() |
slideInHorizontally() | slideOutHorizontally() |
slideInVertically() | slideOutVertically() |
expandIn() | shrinkOut() |
expandHorizontally() | shrinkHorizontally() |
expandVertically() | shrinkVertically() |
scaleIn() | scaleOut() |
▶️ C贸mo Ejecutar
- Abre Android Studio
- Crea proyecto con Compose
- Copia el c贸digo
- Presiona Run
- Prueba las diferentes animaciones
馃И Resultado Final
Aplicaci贸n con m煤ltiples tipos de animaciones y transiciones fluidas usando Jetpack Compose.
馃摜 Descargar Proyecto
馃憠
馃檶 Gracias por Visitar mi Blog
✔️ Comp谩rtelo
✔️ D茅jame un comentario
✔️ S铆gueme para m谩s contenido
❓ Preguntas Frecuentes
1. ¿Qu茅 es Jetpack Compose?
Framework moderno de UI declarativa que simplifica el desarrollo de interfaces en Android.
2. ¿Son m谩s f谩ciles las animaciones en Compose?
S铆, Compose hace las animaciones m谩s simples y declarativas que el sistema tradicional de View.
3. ¿Puedo combinar m煤ltiples animaciones?
S铆, usa operadores como + para combinar efectos: slideIn() + fadeIn().
4. ¿Compose reemplaza XML?
Compose es el futuro de UI en Android, pero a煤n puedes usar XML si lo prefieres.

No hay comentarios:
Publicar un comentario