animate*AsState
ile tek bir değeri animasyonla değiştirme
animate*AsState
işlevleri, tek bir değeri canlandırmak 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 geçerli değerden belirtilen değere kadar başlatır.
Aşağıda, bu API kullanılarak alfa animasyonu oluşturma örneği verilmiştir. Hedef değeri animateFloatAsState
içine alarak alfa değeri artık sağlanan değerler (bu örnekte 1f
veya 0.5f
) arasında bir animasyon değeri olur.
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 kesintiyi işlemeniz gerekmediğini unutmayın. Arka planda, bir animasyon nesnesi (yani bir Animatable
örneği) oluşturulur ve çağrı sitesinde hatırlanır. İlk hedef değer, bu nesnenin başlangıç değeri olarak kullanılır.
Bundan sonra, bu composable'a farklı bir hedef değer sağladığınız her seferde, bu değere yönelik bir animasyon otomatik olarak başlatılır. Halihazırda devam eden bir animasyon varsa animasyon, mevcut değerinden (ve hızından) başlar ve hedef değere doğru animasyon yapar. 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 animate*AsState
işlevini Float
, Color
, Dp
, Size
, Offset
, Rect
, Int
, IntOffset
ve IntSize
için sunar. Genel bir tür alan TwoWayConverter
ile 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'e bakın.
Geçişle birden fazla özelliği aynı anda canlandırma
Transition
, alt öğeleri olarak bir veya daha fazla animasyonu 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, tür güvenliğini sağlamak için özel bir enum
türü kullanabilirsiniz. Örneğin:
enum class BoxState { Collapsed, Expanded }
updateTransition
, 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. Durumların her biri için hedef değerleri belirtin.
Bu animate*
işlevler, 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
parametresini iletebilirsiniz. Daha fazla bilgi için AnimationSpec'e 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ığına dair bir sinyal olarak kullanılabilir.
Bazen ilk hedef durumdan farklı bir başlangıç durumu kullanmak isteriz. Bunu yapmak için updateTransition
ile MutableTransitionState
'yi 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şlevin yer aldığı 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 endişeleri ayırmak için kullanışlıdır. Üst geçiş, alt geçişlerdeki tüm animasyon değerlerinin farkında olur.
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ş kullanma
AnimatedVisibility
ve AnimatedContent
Transition
'in uzantı işlevleri olarak kullanılabilir. targetState
for
Transition.AnimatedVisibility
and Transition.AnimatedContent
is derived
from the Transition
, and triggering enter/exit transitions as needed when the
Transition
's targetState
has changed. Bu uzantı işlevleri, aksi takdirde AnimatedVisibility
/AnimatedContent
içinde dahili 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
'nin durum değişikliği dışarıdan gözlemlenebilir. visible
parametresi yerine, AnimatedVisibility
'nin bu sürümü, ü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 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şi kapsülleme ve yeniden kullanılabilir hale getirme
Basit kullanım alanlarında, kullanıcı arayüzünüzle aynı composable'da geçiş animasyonları tanımlamak tamamen geçerli bir seçenektir. Ancak, bir dizi animasyonlu değer içeren karmaşık bir bileşen üzerinde çalışırken animasyon uygulamasını composable kullanıcı arayüzünden ayırmak isteyebilirsiniz.
Bunu, tüm animasyon değerlerini içeren bir sınıf ve bu sınıfın bir örneğini döndüren bir "güncelleme" işlevi oluşturarak yapabilirsiniz. Geçiş uygulaması yeni ayrı işleve çıkarılabilir. Bu desen, animasyon mantığını merkezileştirmek veya karmaşık animasyonları yeniden kullanılabilir hale getirmek gerektiğinde faydalı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 döngüye giren bir 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ıkları sürece durmaz. InfiniteTransition
örneğini rememberInfiniteTransition
ile oluşturabilirsiniz. Alt animasyonlar animateColor
, animatedFloat
veya animatedValue
ile eklenebilir. 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 düzeyli 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 kurulmuştur.
animate*AsState
işlevleri, anlık değer değişikliğini animasyon değeri olarak işleyen en basit API'lerdir. Tek bir değeri canlandırmak için kullanılan, eş yordama dayalı 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 sonsuza kadar çalışmaya devam eden birden fazla animasyonu yönetebilen sonsuz bir geçiş oluşturur. Bu API'lerin tümü Animatable
hariç composable'dır. Bu nedenle, bu animasyonlar composable dışında oluşturulabilir.
Bu API'lerin tümü daha temel olan Animation
API'sine dayanmaktadır. Çoğu uygulama Animation
ile doğrudan etkileşimde bulunmasa da Animation
'nın bazı özelleştirme özellikleri daha üst düzey API'ler aracılığıyla kullanılabilir. AnimationVector
ve AnimationSpec
hakkında daha fazla bilgi için Animasyonları özelleştirme başlıklı makaleye bakın.
Animatable
: Eş yordama dayalı tek değerli animasyon
Animatable
, animateTo
aracılığıyla değiştirilen değeri canlandırabilen bir değer tutucudur. Bu, animate*AsState
uygulamasını destekleyen API'dir.
Bu işlev, tutarlı devamlılık ve karşılıklı dışlama sağlar. Yani değer değişikliği her zaman kesintisizdir ve devam eden tüm animasyonlar iptal edilir.
Animatable
'nın animateTo
dahil birçok özelliği askıya alma işlevi olarak sunulur. Bu nedenle, uygun bir coroutine kapsamına alınmaları gerekir. Örneğin, LaunchedEffect
composable'ı kullanarak yalnızca belirtilen anahtar değerin süresi boyunca geçerli olacak bir 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, Animatable
öğesinin Color.Gray
başlangıç değeriyle bir örneğini oluşturup hatırlıyoruz. Boole işaretinin değerine bağlı olarak
ok
, renk Color.Green
veya Color.Red
olarak animasyonlanır. Boole değerinde yapılan sonraki değişiklikler, animasyonu diğer renge başlatır. Değer değiştirildiğinde devam eden bir animasyon varsa animasyon iptal edilir ve yeni animasyon, geçerli anlık görüntü değeriyle geçerli hızda başlar.
Bu, önceki bölümde bahsedilen animate*AsState
API'sini destekleyen animasyon uygulamasıdır. animate*AsState
ile karşılaştırıldığında, Animatable
doğrudan kullanıldığında çeşitli açılardan daha ayrıntılı kontrol elde edilebilir. İlk olarak, 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österiliyor. Bu kutu, hemen yeşil veya kırmızı renkte animasyon yapmaya başlıyor. İ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, animasyonun tek doğru kaynağı olmadığı ve dokunma etkinlikleri gibi diğer durumlarla senkronize edilmesi gerektiğinde kullanışlıdır. animateDecay
, verilen 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 başlıklı makaleyi inceleyin.
Animatable
, varsayılan olarak Float
ve Color
'ı destekler ancak TwoWayConverter
sağlanarak herhangi bir veri türü kullanılabilir. Daha fazla bilgi için AnimationVector'a bakın.
AnimationSpec
sağlayarak animasyon özelliklerini özelleştirebilirsiniz.
Daha fazla bilgi için AnimationSpec'e bakın.
Animation
: Manuel olarak kontrol edilen animasyon
Animation
, kullanılabilen en düşük düzeyli Animation API'dir. Şimdiye kadar gördüğümüz animasyonların çoğu Animation sınıfı üzerine kurulmuştur. İ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 bilgisi içermez ve yaşam döngüsü kavramı yoktur. Bu API, üst düzey API'lerin kullandığı bir animasyon hesaplama motoru olarak işlev görür.
TargetBasedAnimation
Diğer API'ler çoğu kullanım alanını kapsar ancak TargetBasedAnimation
doğrudan kullanıldığında animasyon oynatma süresini kendiniz kontrol edebilirsiniz. Aşağıdaki örnekte, TargetAnimation
öğesinin oynatma süresi, withFrameNanos
tarafından sağlanan kare süresine göre manuel olarak kontrol ediliyor.
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ın aksine,
DecayAnimation
için targetValue
sağlanması gerekmez. Bunun yerine, targetValue
, initialVelocity
ve initialValue
tarafından belirlenen başlangıç koşullarına ve sağlanan DecayAnimationSpec
'e göre hesaplanır.
Çürüme animasyonları, öğeleri durdurmak için genellikle fırlatma hareketinden sonra kullanılır. Animasyon hızı, initialVelocityVector
ile 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 {:#customize-animations}
- Compose'da animasyonlar
- Animasyon değiştiriciler ve composable'lar