Compose'da birçok yerleşik animasyon mekanizması vardır ve hangisinin seçileceğini belirlemek zor olabilir. Aşağıda, animasyonların yaygın kullanım alanlarının bir listesi yer almaktadır. Kullanabileceğiniz farklı API seçeneklerinin tümü hakkında daha ayrıntılı bilgi için Animasyon Oluşturma dokümanlarının tamamını okuyun.
Ortak composable özelliklerini canlandırma
Compose, birçok yaygın animasyon kullanım alanında çözüm bulmanıza olanak tanıyan kullanışlı API'ler sunar. Bu bölümde, bir composable'ın ortak özelliklerini nasıl canlandırabileceğiniz gösterilmektedir.
Görünen / kaybolan animasyon
Bir Besteci'yi gizlemek veya göstermek için AnimatedVisibility
simgesini kullanın. AnimatedVisibility
içindeki çocuklar 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 giriş ve çıkış parametreleri, bir composable'ın görünüp kaybolduğunda nasıl davranacağını yapılandırmanıza olanak tanır. Daha fazla bilgi için tüm belgeleri okuyun.
Bir composable'ın görünürlüğünü canlandırmanın bir diğer yolu da animateFloatAsState
kullanarak alfayı zaman içinde 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) ) { }
Bununla birlikte, alfayı değiştirmek, composable'ın bestede kaldığı ve yerleştirildiği alanı kapmaya devam ettiği uyarısını da beraberinde getirir. Bu durum, ekran okuyucuların ve diğer erişilebilirlik mekanizmalarının ekrandaki öğeyi düşünmeye devam etmesine yol açabilir. Diğer yandan, AnimatedVisibility
sonuçta bu öğeyi besteden kaldırır.
Arka plan rengini canlandır
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()
kullanımına kıyasla daha etkilidir.
Modifier.background()
, tek seferlik renk ayarı için kabul edilebilir. Ancak bir renk zaman içinde canlandırılırken bu durum, gereğinden fazla yeniden besteye neden olabilir.
Arka plan rengini sonsuza kadar canlandırmak için animasyonları tekrarlama bölümünü inceleyin.
Bir composable'ın boyutunu canlandırma
Oluşturma, composable'ların boyutunu birkaç farklı şekilde canlandırmanızı sağlar. Birleştirilebilir boyut değişiklikleri arasındaki animasyonlar için animateContentSize()
değerini kullanın.
Örneğin, birden çok satıra genişleyebilen metin içeren bir kutunuz varsa daha yumuşak bir geçiş için Modifier.animateContentSize()
öğesini 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 yapılması gerektiğini açıklamak için AnimatedContent
ile birlikte SizeTransform
kullanabilirsiniz.
Bir composable'ın konumunu canlandır
Bir composable'ın konumuna animasyon eklemek için Modifier.offset{ }
öğesini animateIntOffsetAsState()
ile birlikte 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 } )
Konumu veya boyutu canlandırırken composable'ların diğer composable'ların üzerine veya altına çizilmediğinden emin olmak için Modifier.layout{ }
kullanın. Bu değiştirici, boyut ve konum değişikliklerini üst öğeye yayar ve ardından diğer alt öğeleri etkiler.
Örneğin, bir Box
öğesini Column
içinde taşıyorsanız ve Box
hareket ettiğinde diğer alt öğelerin de hareket etmesi gerekiyorsa ofset bilgilerini Modifier.layout{ }
ile 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) ) }
Bir composable'ın dolgusunu canlandırma
Bir composable'ın dolgusunu canlandırmak için animateDpAsState
öğesini Modifier.padding()
ile birlikte 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 } )
Bir composable'ın yüksekliğini gösterme
Bir composable'ın yüksekliğine animasyon eklemek için animateDpAsState
öğesini Modifier.graphicsLayer{ }
ile birlikte kullanın. Tek seferlik yükseklik değişiklikleri için Modifier.shadow()
değerini kullanın. Gölgeyi canlandırıyorsanız Modifier.graphicsLayer{ }
değiştiricisini kullanmak daha yüksek performanslı 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'ı kullanın ve yükseltme özelliğini durum başına farklı değerlere ayarlayın.
Metin ölçeğini, çevirisini veya döndürmeyi canlandırma
Metnin ölçeğini, çevrilmesini veya döndürülmesini canlandırırken TextStyle
öğesindeki textMotion
parametresini TextMotion.Animated
olarak ayarlayın. Bu sayede metin animasyonları
arasında daha yumuşak geçişler sağlanır. Metni çevirmek, döndürmek veya ölçeklendirmek için Modifier.graphicsLayer{ }
aracını 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ır
Metin rengine animasyon eklemek için BasicText
composable'da color
lambda 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ş yapın
Farklı composable'lar arasında animasyon oluşturmak için AnimatedContent
kullanın. composable'lar arasında yalnızca standart 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 türünü gösterecek şekilde özelleştirilebilir. Daha fazla bilgi için AnimatedContent
ile ilgili dokümanları veya şuradaki blog yayınını okuyun:
AnimatedContent
.
Farklı hedeflere giderken animasyon kullan
Gezinme-compose yapısını kullanırken composable'lar arasındaki geçişleri canlandırmak için bir composable'da enterTransition
ve exitTransition
özelliklerini belirtin. Üst düzey NavHost
, tüm hedefler için kullanılacak varsayılan animasyonu da 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 olarak tekrarlamak için rememberInfiniteTransition
öğesini infiniteRepeatable
animationSpec
ile kullanın. RepeatModes
öğesini değiştirerek nasıl ileri geri gitmesi gerektiğini belirtin.
Belirli sayıda tekrarlamak için finiteRepeatable
öğesini 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şlat
LaunchedEffect
, besteye bir composable girdiğinde çalışır. Bir composable'ın lansmanı yapıldığında animasyon başlatır. Animasyon durumu değişikliğini sağlamak için bunu kullanabilirsiniz. Başlatma sırasında animasyonu başlatmak için Animatable
ile animateTo
yöntemi kullanıldığında:
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ş zamanlı API'lerini kullanın. Diğerinden sonra Animatable
için animateTo
çağrısının yapılması, her animasyonun devam etmeden önce önceki animasyonların tamamlanmasını beklemesine neden olur .
Bunun nedeni, bunun bir 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ş anlamlı API'leri (Animatable#animateTo()
veya animate
) ya da Transition
API'yi kullanın. Eş yordamlarda birden çok başlatma işlevi kullanırsanız animasyonları aynı anda başlatır:
val alphaAnimation = remember { Animatable(0f) } val yAnimation = remember { Animatable(0f) } LaunchedEffect("animationKey") { launch { alphaAnimation.animateTo(1f) } launch { yAnimation.animateTo(100f) } }
Aynı anda birçok farklı özellik animasyonunu çalıştırmak amacıyla aynı durumu kullanmak için updateTransition
API'yi kullanabilirsiniz. Aşağıdaki örnekte, durum değişikliği (rect
ve borderWidth
) tarafından kontrol edilen iki özellik 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 et
Compose'daki animasyonlar performans sorunlarına neden olabilir. Bunun nedeni, animasyonun doğası gereği hareket illüzyonu oluşturmak için ekrandaki pikselleri kare kare hızlı bir şekilde taşımak veya değiştirmektir.
İçerik oluşturmanın farklı aşamalarını göz önünde bulundurun: kompozisyon, düzen ve çizim. Animasyonunuz düzen aşamasını değiştirirse etkilenen tüm composable'ların yeniden düzenlenip yeniden çizilmesi gerekir. Animasyonunuz çizim aşamasında gerçekleşiyorsa, animasyonun düzen aşamasında çalıştırılmasına göre varsayılan olarak daha yüksek performanslı olacaktır. Çünkü, genel olarak yapılması gereken daha az iş olur.
Uygulamanızın animasyon yaparken mümkün olduğunca az performans göstermesini sağlamak için mümkün olduğunda bir Modifier
'nin lambda sürümünü seçin. Bu işlem, yeniden bestelemeyi atlar ve animasyonu beste 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ılır. Bu konuda daha fazla bilgi için performans belgelerindeki okuma işlemlerini erteleme bölümüne bakın.
Animasyon zamanlamasını değiştirme
Oluşturma işleminde varsayılan olarak, çoğu animasyon için yay animasyonları kullanılır. Yaylar ya da fizik tabanlı
animasyonlar daha doğal görünür. Ayrıca sabit bir süre yerine nesnenin o anki hızı da hesaba katıldığından kesintiye uğrayabilirler.
Varsayılanı geçersiz kılmak isterseniz yukarıda gösterilen tüm animasyon API'leri, belirli bir süre boyunca yürütülmesini veya daha fazla atlama yapmasını istediğinizde animasyonun çalışma şeklini özelleştirmek için animationSpec
ayarlayabilir.
Aşağıda, farklı animationSpec
seçeneklerinin özeti verilmiştir:
spring
: Tüm animasyonlar için varsayılan olan fizik tabanlı animasyon. Farklı bir animasyon görünümü ve hissi elde etmek için sertliği veya dampingRatio'yu değiştirebilirsiniz.tween
(arasında): Süreye dayalı animasyon, iki değer arasında birEasing
işleviyle animasyon.keyframes
: Bir animasyonda belirli temel noktalarda değerleri belirtmeyle ilgili spesifikasyon.repeatable
:RepeatMode
tarafından belirtilen, belirli sayıda çalıştırılan süreye dayalı spesifikasyon.infiniteRepeatable
: Sonsuza kadar çalışan, süreye dayalı spesifikasyon.snap
: Animasyon olmadan bitiş değerine anında tutturur.
animationSpecs hakkında daha fazla bilgi için dokümanların tamamını okuyun.
Ek kaynaklar
Compose'daki eğlenceli animasyonlarla ilgili daha fazla örnek için aşağıdakilere göz atın:
- Oluştur'da 5 hızlı animasyon
- Compose'da denizanalarını hareket ettirme
- Compose'da
AnimatedContent
özelliğini özelleştirme - Oluşturma'daki Yumuşak Geçiş işlevlerinde yumuşak geçiş