animate*AsState
ile tek bir değeri canlandırın
animate*AsState
işlevleri, tek bir değeri animasyonlu hale getirmek için Compose'daki en 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 doğru başlatır.
Aşağıda, bu API'yi kullanarak alfa testi uygulama örneği verilmiştir. Hedef değeri animateFloatAsState
içine sarmalayarak alfa değeri, sağlanan değerler (bu durumda 1f
veya 0.5f
) arasındaki bir animasyon değeri haline gelir.
var enabled by remember { mutableStateOf(true) } val alpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f, label = "alpha") Box( Modifier .fillMaxSize() .graphicsLayer(alpha = alpha) .background(Color.Red) )
Herhangi bir animasyon sınıfının örneğini oluşturmanıza veya kesintiyi yönetmenize gerek olmadığını unutmayın. Arka planda, ilk hedef değer başlangıç değeri olarak bir animasyon nesnesi (yani bir Animatable
örneği) oluşturulur ve çağrı sitesinde hatırlanır. Bundan sonra, bu bileşene farklı bir hedef değer sağladığınızda o değere doğru otomatik olarak bir animasyon başlatılır. Yayında zaten bir animasyon varsa animasyon geçerli değerinden (ve hızından) başlar ve hedef değere doğru canlandırılır. Animasyon sırasında bu kompozisyon yeniden oluşturulur ve her karede güncellenmiş bir animasyon değeri döndürür.
Compose, Float
, Color
, Dp
, Size
, Offset
, Rect
, Int
, IntOffset
veIntSize
için animate*AsState
işlevleri sunar. Genel bir tür alan TwoWayConverter
to animateValueAsState
sağlayarak diğer veri türleri için kolayca destek ekleyebilirsiniz.
AnimationSpec
sağlayarak animasyon özelliklerini özelleştirebilirsiniz.
Daha fazla bilgi için AnimationSpec bölümüne bakın.
Geçişle birden fazla mülkü aynı anda canlandırma
Transition
, bir veya daha fazla animasyonu alt öğesi olarak yönetir ve bunları birden fazla durumda aynı anda çalıştırır.
Eyaletler herhangi bir veri türünde olabilir. Aşağıdaki örnekte olduğu gibi çoğu durumda, tür güvenliğini sağlamak için özel bir enum
türü kullanabilirsiniz:
enum class BoxState { Collapsed, Expanded }
updateTransition
, Transition
örneğini 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. Eyaletlerin her biri 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 bir transitionSpec
parametresi iletebilirsiniz. Daha fazla bilgi için AnimationSpec bölümüne 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 } }
Bir geçiş hedef duruma ulaştığında Transition.currentState
, Transition.targetState
ile aynı olur. Bu, geçişin tamamlanıp tamamlanmadığını gösteren bir sinyal olarak kullanılabilir.
Bazen ilk hedef durumdan farklı bir başlangıç durumu olmasını isteriz. Bunu yapmak için updateTransition
'ü MutableTransitionState
ile birlikte kullanabiliriz. Örneğin, kod kompozisyona girer girmez animasyonu başlatmamı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şlevi içeren daha karmaşık bir geçiş için alt geçiş oluşturmak üzere createChildTransition
kullanabilirsiniz. Bu teknik, karmaşık bir bileşimde birden fazla alt bileşen arasında endişeleri ayırmak için kullanışlıdır. Üst geçiş, alt geçişlerdeki tüm animasyon değerlerini algılar.
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 } ) } }
Geçişi AnimatedVisibility
ve AnimatedContent
ile kullanma
AnimatedVisibility
ve AnimatedContent
, Transition
'nin 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ış geçişlerini gerektiği şekilde tetikler. Bu uzantı işlevleri, aksi takdirde AnimatedVisibility
/AnimatedContent
içinde olacak tüm giriş/çıkış/boyut dönüştürme animasyonlarının Transition
içine yerleştirilmesine olanak tanır.
Bu uzantı işlevleriyle AnimatedVisibility
/AnimatedContent
'un durum değişikliği dışarıdan gözlemlenebilir. Boole visible
parametresi yerine, bu AnimatedVisibility
sürümü, üst geçişin hedef durumunu boole'ye dönüştüren bir lambda alır.
Ayrıntılar için AnimatedVisibility ve AnimatedContent başlıklı makaleleri inceleyin.
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şleri sarmalayarak yeniden kullanılabilir hale getirme
Basit kullanım alanlarında, geçiş animasyonlarını kullanıcı arayüzünüzle aynı composable'da tanımlamak son derece geçerli bir seçenektir. Ancak, animasyonlu değerlerin bulunduğu karmaşık bir bileşen üzerinde çalışırken animasyon uygulamasını, birleştirilebilir kullanıcı arayüzünden ayırmak isteyebilirsiniz.
Bunu, tüm animasyon değerlerini barındıran bir sınıf ve bu sınıfın bir örneğini döndüren bir "update" işlevi oluşturarak yapabilirsiniz. Geçiş uygulaması, yeni ayrı işleve ayıklanabilir. Bu desen, animasyon mantığını merkezileştirmek veya karmaşık animasyonları yeniden kullanılabilir hale getirmek 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 animasyon oluşturma
InfiniteTransition
, Transition
gibi bir veya daha fazla alt animasyon içerir ancak animasyonlar, kompozisyona girer girmez çalışmaya başlar ve kaldırılmadıkça durmazlar. rememberInfiniteTransition
ile InfiniteTransition
örneği oluşturabilirsiniz. Alt animasyonlar animateColor
, animatedFloat
veya animatedValue
ile eklenebilir. Animasyon özelliklerini belirtmek için 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) )
Alt düzey animasyon API'leri
Önceki bölümde bahsedilen tüm üst düzey animasyon API'leri, düşük düzey animasyon API'lerinin temeli üzerine inşa edilmiştir.
animate*AsState
işlevleri, anlık değer değişikliğini animasyon değeri olarak oluşturarak en basit API'lerdir. Tek bir değeri animasyonlu hale getirmek için iş parçacığı tabanlı bir API olan Animatable
tarafından desteklenir. updateTransition
, birden fazla animasyonlu değeri yönetebilen ve bunları durum değişikliğine göre çalıştırabilen bir geçiş nesnesi oluşturur. rememberInfiniteTransition
benzerdir ancak süresiz olarak çalışmaya devam eden birden fazla animasyonu yönetebilen sonsuz bir geçiş oluşturur. Bu API'lerin tümü, Animatable
hariç olmak üzere derlenebilir. Bu, bu animasyonların kompozisyon dışında oluşturulabileceği anlamına gelir.
Bu API'lerin tümü daha temel Animation
API'ye dayanır. Çoğu uygulama doğrudan Animation
ile etkileşime girmese de Animation
için özelleştirme özelliklerinden bazıları daha üst düzey API'ler aracılığıyla kullanılabilir. AnimationVector
ve AnimationSpec
hakkında daha fazla bilgi için Animasyonları özelleştirme bölümüne bakın.
Animatable
: Eş yordam tabanlı tek değer animasyonu
Animatable
, animateTo
aracılığıyla değiştikçe değeri animasyonlu olarak gösterebilen bir değer tutucusudur. Bu, animate*AsState
'ün uygulanmasını destekleyen API'dir.
Bu, tutarlı bir devamlılık ve karşılıklı münhasırlık sağlar. Yani değer değişikliği her zaman süreklidir ve devam eden animasyonlar iptal edilir.
animateTo
dahil olmak üzere Animatable
'ün birçok özelliği, askıya alma işlevi olarak sağlanır. Bu nedenle, uygun bir coroutine kapsamına alınmaları gerekir. Örneğin, LaunchedEffect
composable'ı kullanarak yalnızca belirtilen anahtar değerinin süresi boyunca kapsam oluşturabilirsiniz.
// 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, başlangıç değeri Color.Gray
olan bir Animatable
örneği oluşturup hatırlıyoruz. Boole işaretçisinin ok
değerine bağlı olarak renk, Color.Green
veya Color.Red
olarak animasyonlu bir şekilde değişir. Boole değerinde yapılan sonraki değişiklikler, diğer renge animasyon başlatır. Değer değiştirildiğinde devam eden bir animasyon varsa animasyon iptal edilir ve yeni animasyon, geçerli hıza sahip geçerli anlık görüntü değerinden başlar.
Bu, önceki bölümde bahsedilen animate*AsState
API'yi yedekleyen animasyon uygulamasıdır. animate*AsState
ile karşılaştırıldığında Animatable
kullanmak, doğrudan bize çeşitli konularda daha ayrıntılı kontrol sağlar. Birincisi, Animatable
ilk hedef değerinden farklı bir başlangıç değerine sahip olabilir.
Örneğin, yukarıdaki kod örneğinde ilk başta gri bir kutu gösterilir ve bu kutu hemen yeşile veya kırmızıya dönmeye başlar. İkinci olarak, Animatable
içerik değeri üzerinde daha fazla işlem (snapTo
ve animateDecay
) sağlar. snapTo
, geçerli değeri hemen hedef değere ayarlar. Bu, animasyon tek doğru kaynak olmadığında 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, fırlatma davranışını uygulamak için kullanışlıdır. Daha fazla bilgi için İşaret ve animasyon bölümüne bakın.
Animatable
, Float
ve Color
'yi destekler ancak TwoWayConverter
sağlanarak her türlü veri türü kullanılabilir. Daha fazla bilgi için AnimationVector bölümüne bakın.
AnimationSpec
sağlayarak animasyon özelliklerini özelleştirebilirsiniz.
Daha fazla bilgi için AnimationSpec öğesine bakın.
Animation
: Manuel olarak kontrol edilen animasyon
Animation
, kullanılabilen en düşük düzey Animasyon API'sidir. Şu ana kadar gördüğümüz animasyonların
çoğu Animasyonun üzerine inşa ediliyor. İki Animation
alt türü vardır:
TargetBasedAnimation
ve DecayAnimation
.
Animation
yalnızca animasyonun süresini manuel olarak kontrol etmek için kullanılmalıdır.
Animation
durum bilgisine sahip değildir ve yaşam döngüsü kavramı yoktur. Daha üst düzey API'lerin kullandığı bir animasyon hesaplama motoru görevi görür.
TargetBasedAnimation
Diğer API'ler çoğu kullanım alanını kapsar ancak TargetBasedAnimation
'ü doğrudan kullanmak, animasyon oynatma süresini kendiniz kontrol etmenize olanak tanır. Aşağıdaki örnekte, TargetAnimation
öğesinin oynatma süresi, withFrameNanos
tarafından sağlanan kare süresine göre manuel olarak kontrol edilir.
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
'ün aksine, DecayAnimation
için targetValue
sağlanmasına gerek yoktur. Bunun yerine, targetValue
değerini initialVelocity
, initialValue
ve sağlanan DecayAnimationSpec
başlangıç koşullarını temel alarak hesaplar.
Çökme animasyonları, öğeleri yavaşlatarak durdurmak için genellikle bir fırlatma hareketinden sonra kullanılır. Animasyon hızı, initialVelocityVector
tarafından belirlenen değerde başlar ve zaman içinde yavaşlar.
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir
- Animasyonları özelleştirme {:#customize-animations}
- Compose'daki animasyonlar
- Animasyon değiştiricileri ve composable'lar