Bu sayfada, Jetpack Compose'da değere dayalı animasyonların nasıl oluşturulacağı açıklanmakta ve değerleri mevcut ve hedef durumlarına göre canlandıran API'lere odaklanılmaktadır.
animate*AsState ile tek bir değere animasyon ekleme
animate*AsState işlevleri, Compose'da tek bir değeri canlandırmak için kullanılan basit animasyon API'leridir. Yalnızca hedef değeri (veya bitiş değerini) sağlarsınız ve API, animasyonu mevcut değerden belirtilen değere başlatır.
Aşağıdaki örnekte, bu API kullanılarak alfa animasyonu oluşturulmaktadır. Hedef değeri animateFloatAsState ile sarmalayarak alfa değerini, sağlanan değerler (bu örnekte 1f veya 0.5f) arasında bir animasyon değeri haline getirdik.
var enabled by remember { mutableStateOf(true) } val animatedAlpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f, label = "alpha") Box( Modifier .fillMaxSize() .graphicsLayer { alpha = animatedAlpha } .background(Color.Red) )
Herhangi bir animasyon sınıfının örneğini oluşturmanız veya kesintileri işlemeniz gerekmez. Arka planda bir animasyon nesnesi (yani bir Animatable örneği) oluşturulur ve ilk hedef değeri başlangıç değeri olarak çağrı sitesinde hatırlanır. Bu noktadan sonra, bu composable'a farklı bir hedef değer verdiğinizde otomatik olarak bu değere doğru bir animasyon başlatılır. Zaten bir animasyon varsa animasyon, mevcut değerinden (ve hızından) başlar ve hedef değere doğru animasyon oluşturur. Animasyon sırasında bu composable yeniden oluşturulur ve her karede güncellenmiş bir animasyon değeri döndürür.
Compose, varsayılan olarak Float, Color, Dp, Size, Offset, Rect, Int, IntOffset ve IntSize için animate*AsState işlevleri sağlar. Genel tür alan animateValueAsState için bir TwoWayConverter sağlayarak diğer veri türleri için destek ekleyebilirsiniz.
AnimationSpec sağlayarak animasyon özelliklerini özelleştirebilirsiniz. Daha fazla bilgi için AnimationSpec sayfasına bakın.
Geçişle birden fazla özelliğe aynı anda animasyon ekleme
Transition, bir veya daha fazla animasyonu alt öğesi olarak yönetir ve bunları birden fazla durum arasında aynı anda çalıştırır.
Durumlar herhangi bir veri türünde olabilir. Çoğu durumda, tip güvenliğini doğrulamak için özel bir enum
tipi kullanabilirsiniz. Örneğin:
enum class BoxState { Collapsed, Expanded }
updateTransition, bir Transition örneği oluşturup hatırlar ve durumunu günceller.
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "box state")
Ardından, bu geçişte bir alt animasyon tanımlamak için animate* uzantı işlevlerinden birini kullanabilirsiniz. Her bir durum için hedef değerleri belirtin.
Bu animate* işlevleri, geçiş durumu updateTransition ile güncellendiğinde animasyon sırasında her karede güncellenen bir animasyon değeri döndürür.
val rect by transition.animateRect(label = "rectangle") { state -> when (state) { BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f) BoxState.Expanded -> Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = "border width") { state -> when (state) { BoxState.Collapsed -> 1.dp BoxState.Expanded -> 0.dp } }
İsteğe bağlı olarak, geçiş durumu değişikliklerinin her bir kombinasyonu için farklı bir AnimationSpec belirtmek üzere transitionSpec parametresi iletebilirsiniz. Daha fazla bilgi için AnimationSpec sayfasına bakın.
val color by transition.animateColor( transitionSpec = { when { BoxState.Expanded isTransitioningTo BoxState.Collapsed -> spring(stiffness = 50f) else -> tween(durationMillis = 500) } }, label = "color" ) { state -> when (state) { BoxState.Collapsed -> MaterialTheme.colorScheme.primary BoxState.Expanded -> MaterialTheme.colorScheme.background } }
Geçiş hedef duruma ulaştığında Transition.currentState, Transition.targetState ile aynı olur. Bunu, geçişin tamamlanıp tamamlanmadığını
gösteren bir sinyal olarak kullanabilirsiniz.
Bazen ilk hedef durumdan farklı bir başlangıç durumu olmasını isteyebilirsiniz. Bunu yapmak için MutableTransitionState ile updateTransition kullanabilirsiniz. Örneğin, kod oluşturma aşamasına girer girmez animasyonu başlatmanıza olanak tanır.
// Start in collapsed state and immediately animate to expanded var currentState = remember { MutableTransitionState(BoxState.Collapsed) } currentState.targetState = BoxState.Expanded val transition = rememberTransition(currentState, label = "box state") // ……
Birden fazla composable işlev içeren daha karmaşık bir geçiş için createChildTransition kullanarak alt geçiş oluşturabilirsiniz. Bu teknik, karmaşık bir composable'daki birden fazla alt bileşen arasındaki sorunları ayırmak için kullanışlıdır. Üst geçiş, alt geçişlerdeki tüm animasyon değerlerini bilir.
enum class DialerState { DialerMinimized, NumberPad } @Composable fun DialerButton(isVisibleTransition: Transition<Boolean>) { // `isVisibleTransition` spares the need for the content to know // about other DialerStates. Instead, the content can focus on // animating the state change between visible and not visible. } @Composable fun NumberPad(isVisibleTransition: Transition<Boolean>) { // `isVisibleTransition` spares the need for the content to know // about other DialerStates. Instead, the content can focus on // animating the state change between visible and not visible. } @Composable fun Dialer(dialerState: DialerState) { val transition = updateTransition(dialerState, label = "dialer state") Box { // Creates separate child transitions of Boolean type for NumberPad // and DialerButton for any content animation between visible and // not visible NumberPad( transition.createChildTransition { it == DialerState.NumberPad } ) DialerButton( transition.createChildTransition { it == DialerState.DialerMinimized } ) } }
AnimatedVisibility ve AnimatedContent ile geçişi kullanma
AnimatedVisibility ve AnimatedContent, Transition öğesinin uzantı işlevleri olarak kullanılabilir. Transition.AnimatedVisibility ve Transition.AnimatedContent için targetState, Transition öğesinden türetilir ve Transition öğesinin targetState değeri değiştiğinde giriş, çıkış ve sizeTransform animasyonlarını gerektiği gibi tetikler. Bu uzantı işlevleri, normalde AnimatedVisibility/AnimatedContent için dahili olan tüm giriş, çıkış ve sizeTransform animasyonlarını Transition içine taşımanıza olanak tanır. Bu uzantı işlevleriyle AnimatedVisibility/AnimatedContent'nin durum değişikliğini dışarıdan gözlemleyebilirsiniz. AnimatedVisibility işlevinin bu sürümü, boole visible parametresi yerine üst geçişin hedef durumunu boole değerine dönüştüren bir lambda alır.
Ayrıntılar için AnimatedVisibility ve AnimatedContent sayfalarına bakın.
var selected by remember { mutableStateOf(false) } // Animates changes when `selected` is changed. val transition = updateTransition(selected, label = "selected state") val borderColor by transition.animateColor(label = "border color") { isSelected -> if (isSelected) Color.Magenta else Color.White } val elevation by transition.animateDp(label = "elevation") { isSelected -> if (isSelected) 10.dp else 2.dp } Surface( onClick = { selected = !selected }, shape = RoundedCornerShape(8.dp), border = BorderStroke(2.dp, borderColor), shadowElevation = elevation ) { Column( modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Text(text = "Hello, world!") // AnimatedVisibility as a part of the transition. transition.AnimatedVisibility( visible = { targetSelected -> targetSelected }, enter = expandVertically(), exit = shrinkVertically() ) { Text(text = "It is fine today.") } // AnimatedContent as a part of the transition. transition.AnimatedContent { targetState -> if (targetState) { Text(text = "Selected") } else { Icon(imageVector = Icons.Default.Phone, contentDescription = "Phone") } } } }
Geçişi kapsülleme ve yeniden kullanılabilir hale getirme
Basit kullanım alanları için geçiş animasyonlarını kullanıcı arayüzünüzle aynı composable'da tanımlamak geçerli bir seçenektir. Ancak animasyonlu değerleri olan karmaşık bir bileşen üzerinde çalışırken animasyon uygulamasını composable kullanıcı arayüzünden ayırmak isteyebilirsiniz.
Bunu yapmak için tüm animasyon değerlerini içeren bir sınıf ve bu sınıfın bir örneğini döndüren bir update işlevi oluşturabilirsiniz. Geçiş uygulamasını yeni ve ayrı bir işleve çıkarabilirsiniz. Bu kalıp, animasyon mantığını merkezileştirmeniz veya karmaşık animasyonları yeniden kullanılabilir hale getirmeniz gerektiğinde kullanışlıdır.
enum class BoxState { Collapsed, Expanded } @Composable fun AnimatingBox(boxState: BoxState) { val transitionData = updateTransitionData(boxState) // UI tree Box( modifier = Modifier .background(transitionData.color) .size(transitionData.size) ) } // Holds the animation values. private class TransitionData( color: State<Color>, size: State<Dp> ) { val color by color val size by size } // Create a Transition and return its animation values. @Composable private fun updateTransitionData(boxState: BoxState): TransitionData { val transition = updateTransition(boxState, label = "box state") val color = transition.animateColor(label = "color") { state -> when (state) { BoxState.Collapsed -> Color.Gray BoxState.Expanded -> Color.Red } } val size = transition.animateDp(label = "size") { state -> when (state) { BoxState.Collapsed -> 64.dp BoxState.Expanded -> 128.dp } } return remember(transition) { TransitionData(color, size) } }
rememberInfiniteTransition ile sonsuz tekrarlanan bir animasyon oluşturun
InfiniteTransition, Transition gibi bir veya daha fazla alt animasyon içerir ancak animasyonlar, oluşturmaya girer girmez çalışmaya başlar ve kaldırılmadıkları sürece durmaz. rememberInfiniteTransition ile bir InfiniteTransition örneği oluşturabilir ve animateColor, animateFloat veya animateValue ile alt animasyonlar ekleyebilirsiniz. Animasyon özelliklerini belirtmek için bir infiniteRepeatable da belirtmeniz gerekir.
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val color by infiniteTransition.animateColor( initialValue = Color.Red, targetValue = Color.Green, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "color" ) Box( Modifier .fillMaxSize() .background(color) )
Düşük seviyeli animasyon API'leri
Önceki bölümde bahsedilen tüm üst düzey animasyon API'leri, alt düzey animasyon API'lerini temel alır.
animate*AsState işlevleri, anlık değer değişikliğini animasyon değeri olarak oluşturan basit API'lerdir. Bu işlev, tek bir değeri canlandırmak için eş yordam tabanlı bir API olan Animatable tarafından desteklenir.
updateTransition, birden fazla animasyon değerini yönetebilen ve bir durum değiştiğinde bunları çalıştırabilen bir geçiş nesnesi oluşturur. rememberInfiniteTransition
benzerdir ancak süresiz olarak devam eden birden fazla animasyonu yönetebilen sonsuz bir geçiş oluşturur. Animatable hariç tüm bu API'ler composable'dır. Yani bu animasyonları oluşturma dışında oluşturabilirsiniz.
Tüm bu API'ler, daha temel olan Animation API'yi temel alır. Çoğu uygulama Animation ile doğrudan etkileşim kurmasa da daha üst düzey API'ler aracılığıyla özelleştirme özelliklerinden bazılarına erişebilirsiniz. AnimationVector ve AnimationSpec hakkında daha fazla bilgi için Animasyonları özelleştirme başlıklı makaleyi inceleyin.
Animatable: Coroutine tabanlı tek değer animasyonu
Animatable, animateTo kullanılarak değiştirilen değeri animasyonlu hale getirebilen bir değer tutucudur. Bu API, animate*AsState uygulamasını destekler. Bu sayede, tutarlı bir devamlılık ve karşılıklı dışlama sağlanır. Yani değer değişimi her zaman süreklidir ve Compose, devam eden animasyonları iptal eder.
animateTo dahil olmak üzere Animatable uygulamasının birçok özelliği askıya alma işlevleridir.
Yani bunları uygun bir eş yordam kapsamına sarmalamanız gerekir. Örneğin, yalnızca belirtilen anahtar değerinin süresi için bir kapsam oluşturmak üzere LaunchedEffect composable'ını kullanabilirsiniz.
// Start out gray and animate to green/red based on `ok` val color = remember { Animatable(Color.Gray) } LaunchedEffect(ok) { color.animateTo(if (ok) Color.Green else Color.Red) } Box( Modifier .fillMaxSize() .background(color.value) )
Yukarıdaki örnekte, ilk değeri Color.Gray olan bir Animatable örneği oluşturup hatırlarsınız. ok boole işaretinin değerine bağlı olarak renk, Color.Green veya Color.Red olarak animasyonlu şekilde değişir. Boole değerinde yapılan sonraki değişiklikler, diğer renge geçiş animasyonunu başlatır.
Değer değiştiğinde devam eden bir animasyon varsa Compose bu animasyonu iptal eder ve yeni animasyon, mevcut hızla mevcut anlık görüntü değerinden başlar.
Bu Animatable API, önceki bölümde bahsedilen animate*AsState için temel uygulamadır. Doğrudan Animatable kullanmak, çeşitli şekillerde daha ayrıntılı kontrol imkanı sunar:
- İlk olarak,
Animatableilk hedef değerinden farklı bir başlangıç değerine sahip olabilir. Örneğin, önceki kod örneğinde ilk başta gri bir kutu gösterilir ve bu kutu hemen yeşil veya kırmızı renge dönüşür. - İkinci olarak
Animatable, içerik değeri üzerinde daha fazla işlem (özelliklesnapToveanimateDecay) sağlar.snapTo, mevcut değeri hemen hedef değere ayarlar. Bu, animasyonun tek doğruluk kaynağı olmadığı ve dokunma etkinlikleri gibi diğer durumlarla senkronize edilmesi gerektiğinde kullanışlıdır.animateDecay, belirtilen hızdan yavaşlayan bir animasyon başlatır. Bu, kaydırma davranışını uygulamak için kullanışlıdır.
Daha fazla bilgi için Hareket ve animasyon bölümüne bakın.
Varsayılan olarak Animatable, Float ve Color türlerini destekler ancak TwoWayConverter sağlayarak herhangi bir veri türünü kullanabilirsiniz. Daha fazla bilgi için AnimationVector sayfasına bakın.
AnimationSpec sağlayarak animasyon özelliklerini özelleştirebilirsiniz.
Daha fazla bilgi için AnimationSpec sayfasına bakın.
Animation: Manuel olarak kontrol edilen animasyon
Animation, mevcut en düşük düzey Animation API'dir. Şimdiye kadar gördüğümüz animasyonların çoğu Animation üzerine kuruludur. İki Animation alt türü vardır: TargetBasedAnimation ve DecayAnimation.
Animation özelliğini yalnızca animasyonun süresini manuel olarak kontrol etmek için kullanın. Animation durum bilgisizdir ve yaşam döngüsü kavramı yoktur. Daha üst düzey API'ler için animasyon hesaplama motoru olarak kullanılır.
TargetBasedAnimation
Diğer API'ler çoğu kullanım alanını kapsar ancak doğrudan TargetBasedAnimation kullanmak, animasyonun oynatma süresini kontrol etmenize olanak tanır. Aşağıdaki örnekte, withFrameNanos tarafından sağlanan kare süresine göre TargetAnimation öğesinin oynatma süresini manuel olarak kontrol edersiniz.
val anim = remember { TargetBasedAnimation( animationSpec = tween(200), typeConverter = Float.VectorConverter, initialValue = 200f, targetValue = 1000f ) } var playTime by remember { mutableLongStateOf(0L) } LaunchedEffect(anim) { val startTime = withFrameNanos { it } do { playTime = withFrameNanos { it } - startTime val animationValue = anim.getValueFromNanos(playTime) } while (someCustomCondition()) }
DecayAnimation
TargetBasedAnimation'nin aksine DecayAnimation, targetValue sağlanmasını gerektirmez. Bunun yerine, initialVelocity ve initialValue tarafından belirlenen başlangıç koşullarına ve sağlanan DecayAnimationSpec değerine göre targetValue değerini hesaplar.
Azalma animasyonları genellikle kaydırma hareketinden sonra öğeleri yavaşlatarak durdurmak için kullanılır. Animasyon hızı, initialVelocityVector tarafından ayarlanan değerde başlar ve zamanla yavaşlar.
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir.
- Animasyonları özelleştirme
- Compose'da animasyonlar
- Animasyon değiştiriciler ve composable'lar