Compose'da birçok yerleşik animasyon mekanizması vardır ve hangisini seçeceğinizi bilmek zor olabilir. Aşağıda, animasyonun yaygın kullanım alanlarının bir listesi verilmiştir. Kullanabileceğiniz farklı API seçeneklerinin tamamı hakkında daha ayrıntılı bilgi için Compose Animation belgelerinin tamamını okuyun.
Yaygın composable özelliklere animasyon ekleme
Compose, birçok yaygın animasyon kullanım alanını çözmenize olanak tanıyan kullanışlı API'ler sunar. Bu bölümde, bir composable'ın yaygın özelliklerini nasıl animasyonlu hale getirebileceğiniz gösterilmektedir.
Görünme / kaybolma animasyonu
Bir Composable'ı gizlemek veya göstermek için AnimatedVisibility kullanın. AnimatedVisibility içindeki alt öğeler, kendi giriş veya çıkış geçişleri için Modifier.animateEnterExit() kullanabilir.
var visible by remember { mutableStateOf(true) } // Animated visibility will eventually remove the item from the composition once the animation has finished. AnimatedVisibility(visible) { // your composable here // ... }
AnimatedVisibility öğesinin enter ve exit parametreleri, bir composable'ın göründüğünde ve kaybolduğunda nasıl davranacağını yapılandırmanıza olanak tanır. Daha fazla bilgi için belgelerin tamamını okuyun.
Bir composable'ın görünürlüğünü canlandırmanın başka bir yolu da animateFloatAsState kullanarak zaman içinde alfayı canlandırmaktır:
var visible by remember { mutableStateOf(true) } val animatedAlpha by animateFloatAsState( targetValue = if (visible) 1.0f else 0f, label = "alpha" ) Box( modifier = Modifier .size(200.dp) .graphicsLayer { alpha = animatedAlpha } .clip(RoundedCornerShape(8.dp)) .background(colorGreen) .align(Alignment.TopCenter) ) { }
Ancak alfa değerini değiştirirken, composable'ın oluşturmada kaldığını ve yerleştirildiği alanı kaplamaya devam ettiğini unutmayın. Bu durum, ekran okuyucuların ve diğer erişilebilirlik mekanizmalarının öğeyi hâlâ ekranda olarak değerlendirmesine neden olabilir. Öte yandan AnimatedVisibility, öğeyi sonunda kompozisyondan kaldırır.
Arka plan rengine animasyon ekle
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Bu seçenek, Modifier.background() kullanmaktan daha yüksek performans sağlar.
Modifier.background(), tek seferlik renk ayarı için kabul edilebilir ancak bir rengi zaman içinde canlandırırken gereğinden fazla yeniden oluşturmaya neden olabilir.
Arka plan rengini sonsuz animasyonlu hale getirmek için animasyon tekrarlama bölümüne bakın.
Bir composable'ın boyutunu animasyonlu hale getirme
Compose, composable'ların boyutunu birkaç farklı şekilde canlandırmanıza olanak tanır. Oluşturulabilir boyut değişiklikleri arasındaki animasyonlar için
animateContentSize() kodunu kullanın.
Örneğin, bir satırdan birden fazla satıra genişleyebilen metin içeren bir kutunuz varsa daha yumuşak bir geçiş elde etmek için Modifier.animateContentSize() kullanabilirsiniz:
var expanded by remember { mutableStateOf(false) } Box( modifier = Modifier .background(colorBlue) .animateContentSize() .height(if (expanded) 400.dp else 200.dp) .fillMaxWidth() .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { expanded = !expanded } ) { }
Boyut değişikliklerinin nasıl gerçekleşmesi gerektiğini açıklamak için SizeTransform ile AnimatedContent özelliğini de kullanabilirsiniz.
Birleştirilebilir öğenin konumuna animasyon ekleme
Bir composable'ın konumuna animasyon eklemek için Modifier.offset{ } ile birlikte animateIntOffsetAsState() kullanın.
var moved by remember { mutableStateOf(false) } val pxToMove = with(LocalDensity.current) { 100.dp.toPx().roundToInt() } val offset by animateIntOffsetAsState( targetValue = if (moved) { IntOffset(pxToMove, pxToMove) } else { IntOffset.Zero }, label = "offset" ) Box( modifier = Modifier .offset { offset } .background(colorBlue) .size(100.dp) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { moved = !moved } )
Konum veya boyut animasyonu sırasında composable'ların diğer composable'ların üzerine veya altına çizilmediğinden emin olmak istiyorsanız Modifier.layout{ } kullanın. Bu değiştirici, boyut ve konum değişikliklerini üst öğeye yayar. Bu değişiklikler diğer alt öğeleri etkiler.
Örneğin, bir Box öğesini Column içinde taşıyorsanız ve Box taşındığında diğer alt öğelerin de taşınması gerekiyorsa Modifier.layout{ } ile birlikte ofset bilgilerini aşağıdaki gibi ekleyin:
var toggled by remember { mutableStateOf(false) } val interactionSource = remember { MutableInteractionSource() } Column( modifier = Modifier .padding(16.dp) .fillMaxSize() .clickable(indication = null, interactionSource = interactionSource) { toggled = !toggled } ) { val offsetTarget = if (toggled) { IntOffset(150, 150) } else { IntOffset.Zero } val offset = animateIntOffsetAsState( targetValue = offsetTarget, label = "offset" ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) Box( modifier = Modifier .layout { measurable, constraints -> val offsetValue = if (isLookingAhead) offsetTarget else offset.value val placeable = measurable.measure(constraints) layout(placeable.width + offsetValue.x, placeable.height + offsetValue.y) { placeable.placeRelative(offsetValue) } } .size(100.dp) .background(colorGreen) ) Box( modifier = Modifier .size(100.dp) .background(colorBlue) ) }
Modifier.layout{ }Bir composable'ın dolgusuna animasyon ekleme
Bir composable'ın dolgusunu canlandırmak için animateDpAsState ile birlikte Modifier.padding() kullanın:
var toggled by remember { mutableStateOf(false) } val animatedPadding by animateDpAsState( if (toggled) { 0.dp } else { 20.dp }, label = "padding" ) Box( modifier = Modifier .aspectRatio(1f) .fillMaxSize() .padding(animatedPadding) .background(Color(0xff53D9A1)) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { toggled = !toggled } )
Birleştirilebilir öğenin yüksekliğini canlandırma
Bir composable'ın yüksekliğine animasyon eklemek için animateDpAsState ile birlikte Modifier.graphicsLayer{ } kullanın. Tek seferlik yükseklik değişiklikleri için Modifier.shadow() kullanın. Gölgeye animasyon uyguluyorsanız Modifier.graphicsLayer{ } değiştiricisini kullanmak daha performanslı bir seçenektir.
val mutableInteractionSource = remember { MutableInteractionSource() } val pressed = mutableInteractionSource.collectIsPressedAsState() val elevation = animateDpAsState( targetValue = if (pressed.value) { 32.dp } else { 8.dp }, label = "elevation" ) Box( modifier = Modifier .size(100.dp) .align(Alignment.Center) .graphicsLayer { this.shadowElevation = elevation.value.toPx() } .clickable(interactionSource = mutableInteractionSource, indication = null) { } .background(colorGreen) ) { }
Alternatif olarak, Card composable'ını kullanın ve yükseklik özelliğini her durum için farklı değerlere ayarlayın.
Metin ölçeği, çeviri veya döndürme animasyonu ekleme
Metnin ölçeğini, çevirisini veya dönüşünü canlandırırken TextStyle üzerindeki textMotion parametresini TextMotion.Animated olarak ayarlayın. Bu sayede metin animasyonları
arasında daha yumuşak geçişler olur. Metni çevirmek, döndürmek veya ölçeklendirmek için Modifier.graphicsLayer{ } simgesini kullanın.
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val scale by infiniteTransition.animateFloat( initialValue = 1f, targetValue = 8f, animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "scale" ) Box(modifier = Modifier.fillMaxSize()) { Text( text = "Hello", modifier = Modifier .graphicsLayer { scaleX = scale scaleY = scale transformOrigin = TransformOrigin.Center } .align(Alignment.Center), // Text composable does not take TextMotion as a parameter. // Provide it via style argument but make sure that we are copying from current theme style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated) ) }
Metin rengini canlandırma
Metin rengine animasyon eklemek için BasicText composable'ında color lambda'sını kullanın:
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val animatedColor by infiniteTransition.animateColor( initialValue = Color(0xFF60DDAD), targetValue = Color(0xFF4285F4), animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "color" ) BasicText( text = "Hello Compose", color = { animatedColor }, // ... )
Farklı içerik türleri arasında geçiş yapma
Farklı composable'lar arasında animasyon oluşturmak için AnimatedContent kullanın. Composable'lar arasında standart bir geçiş istiyorsanız Crossfade kullanın.
var state by remember { mutableStateOf(UiState.Loading) } AnimatedContent( state, transitionSpec = { fadeIn( animationSpec = tween(3000) ) togetherWith fadeOut(animationSpec = tween(3000)) }, modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { state = when (state) { UiState.Loading -> UiState.Loaded UiState.Loaded -> UiState.Error UiState.Error -> UiState.Loading } }, label = "Animated Content" ) { targetState -> when (targetState) { UiState.Loading -> { LoadingScreen() } UiState.Loaded -> { LoadedScreen() } UiState.Error -> { ErrorScreen() } } }
AnimatedContent, birçok farklı giriş ve çıkış geçişi gösterecek şekilde özelleştirilebilir. Daha fazla bilgi için AnimatedContent ile ilgili dokümanları veya AnimatedContent ile ilgili bu blog yayınını okuyun.
Farklı varış noktalarına giderken animasyonlu gösterim
navigation-compose yapısını kullanırken composable'lar arasındaki geçişleri canlandırmak için composable'da enterTransition ve exitTransition değerlerini belirtin. Ayrıca, tüm hedefler için kullanılacak varsayılan animasyonu en üst düzeyde NavHost ayarlayabilirsiniz:
val navController = rememberNavController() NavHost( navController = navController, startDestination = "landing", enterTransition = { EnterTransition.None }, exitTransition = { ExitTransition.None } ) { composable("landing") { ScreenLanding( // ... ) } composable( "detail/{photoUrl}", arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }), enterTransition = { fadeIn( animationSpec = tween( 300, easing = LinearEasing ) ) + slideIntoContainer( animationSpec = tween(300, easing = EaseIn), towards = AnimatedContentTransitionScope.SlideDirection.Start ) }, exitTransition = { fadeOut( animationSpec = tween( 300, easing = LinearEasing ) ) + slideOutOfContainer( animationSpec = tween(300, easing = EaseOut), towards = AnimatedContentTransitionScope.SlideDirection.End ) } ) { backStackEntry -> ScreenDetails( // ... ) } }
Gelen ve giden içeriğe farklı efektler uygulayan birçok farklı giriş ve çıkış geçişi türü vardır. Daha fazla bilgi için belgelere bakın.
Animasyonu tekrarlama
Animasyonunuzu sürekli tekrarlamak için rememberInfiniteTransition öğesini infiniteRepeatable
animationSpec ile kullanın. İleri geri hareket etme şeklini belirtmek için RepeatModes değerini değiştirin.
Belirli sayıda tekrarlamak için repeatable kullanın.
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val color by infiniteTransition.animateColor( initialValue = Color.Green, targetValue = Color.Blue, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(color) } ) { // your composable here }
Bir composable başlatıldığında animasyon başlatma
LaunchedEffect, bir composable oluşturmaya girdiğinde çalışır. Bir composable başlatıldığında animasyon başlatır. Animasyon durum değişikliğini tetiklemek için bunu kullanabilirsiniz. Animasyonu başlatma sırasında başlatmak için animateTo yöntemiyle Animatable kullanma:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
Sıralı animasyonlar oluşturma
Sıralı veya eşzamanlı animasyonlar gerçekleştirmek için Animatable eş yordam API'lerini kullanın. Animatable üzerinde animateTo yöntemini art arda çağırmak, her animasyonun devam etmeden önce önceki animasyonların bitmesini beklemesine neden olur.
Bunun nedeni, askıya alma işlevi olmasıdır.
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { alphaAnimation.animateTo(1f) yAnimation.animateTo(100f) yAnimation.animateTo(500f, animationSpec = tween(100)) }
Eşzamanlı animasyonlar oluşturma
Eşzamanlı animasyonlar elde etmek için eş yordam API'lerini (Animatable#animateTo() veya animate) ya da Transition API'sini kullanın. Bir eş yordam bağlamında birden fazla başlatma işlevi kullanırsanız animasyonlar aynı anda başlatılır:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Aynı durumu kullanarak aynı anda birçok farklı özellik animasyonu oluşturmak için updateTransition API'yi kullanabilirsiniz. Aşağıdaki örnekte, durum değişikliğiyle kontrol edilen iki özellik (rect ve borderWidth) animasyonlu olarak gösterilmektedir:
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "transition") val rect by transition.animateRect(label = "rect") { state -> when (state) { BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f) BoxState.Expanded -> Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = "borderWidth") { state -> when (state) { BoxState.Collapsed -> 1.dp BoxState.Expanded -> 0.dp } }
Animasyon performansını optimize etme
Compose'daki animasyonlar performans sorunlarına neden olabilir. Bunun nedeni, animasyonun doğasıdır: Ekranda pikselleri hızlıca hareket ettirerek veya değiştirerek kare kare hareket illüzyonu oluşturmak.
Compose'un farklı aşamalarını (oluşturma, düzen ve çizim) göz önünde bulundurun. Animasyonunuz düzen aşamasını değiştiriyorsa etkilenen tüm composable'ların yeniden düzenlenmesi ve yeniden çizilmesi gerekir. Animasyonunuz çizim aşamasında gerçekleşiyorsa varsayılan olarak, animasyonu düzen aşamasında çalıştırmanıza kıyasla daha yüksek performans gösterir. Bunun nedeni, genel olarak daha az iş yapmasıdır.
Uygulamanızın animasyon sırasında mümkün olduğunca az işlem yapmasını sağlamak için mümkün olduğunda Modifier lambda sürümünü seçin. Bu, yeniden oluşturma işlemini atlar ve animasyonu oluşturma aşamasının dışında gerçekleştirir. Aksi takdirde, bu değiştirici her zaman çizim aşamasında çalıştığı için Modifier.graphicsLayer{ } kullanın. Bu konu hakkında daha fazla bilgi için performans belgelerindeki okuma işlemlerini erteleme bölümüne bakın.
Animasyon zamanlamasını değiştirme
Compose, çoğu animasyon için varsayılan olarak yay animasyonlarını kullanır. Yaylar veya fizik tabanlı animasyonlar daha doğal hissettirir. Ayrıca, sabit bir süre yerine nesnenin mevcut hızını hesaba kattıkları için kesintiye uğratılabilirler.
Varsayılanı geçersiz kılmak isterseniz daha önce gösterilen tüm animasyon API'leri, animasyonun nasıl çalışacağını özelleştirmek için animationSpec ayarlama özelliğine sahiptir. Bu sayede animasyonun belirli bir süre boyunca çalışmasını veya daha esnek olmasını sağlayabilirsiniz.
Farklı animationSpec seçeneklerinin özeti aşağıda verilmiştir:
spring: Fizik kurallarına dayalı animasyon, tüm animasyonlar için varsayılan seçenektir. Farklı bir animasyon görünümü ve hissi elde etmek için sertliği veya sönümleme oranını değiştirebilirsiniz.tween(between'in kısaltması): Süreye dayalı animasyon,Easingişleviyle iki değer arasında animasyon oluşturur.keyframes: Animasyondaki belirli önemli noktalarda değerleri belirtmek için kullanılan spesifikasyon.repeatable:RepeatModetarafından belirtilen, belirli sayıda çalışan süreye dayalı spesifikasyon.infiniteRepeatable: Süresiz olarak çalışan, süreye dayalı spesifikasyon.snap: Animasyon olmadan anında bitiş değerine geçer.
animationSpecs hakkında daha fazla bilgi için belgelerin tamamını okuyun.
Ek kaynaklar
Compose'daki eğlenceli animasyonlarla ilgili daha fazla örnek için aşağıdakilere göz atın:
- Compose'da 5 hızlı animasyon
- Compose'da denizanasını hareket ettirme
- Compose'da
AnimatedContentözelleştirme - Compose'da yumuşatma işlevlerini kullanma