Bu sayfada, Jetpack Compose'da değere dayalı animasyonların nasıl oluşturulacağı açıklanmaktadır. Mevcut ve hedef durumlarına göre değerleri canlandıran API'lere odaklanılmaktadır.
animate*AsState
ile tek bir değeri canlandırma
animate*AsState
işlevleri, tek bir değeri canlandırmak için Compose'daki 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 doğru başlatır.
Aşağıdaki örnekte, bu API kullanılarak alfa animasyonu yapılıyor. Hedef değeri animateFloatAsState
içine alarak alfa değeri artık sağlanan değerler (bu örnekte 1f
veya 0.5f
) arasındaki 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 yönetmeniz gerekmez. Arka planda, bir animasyon nesnesi (yani bir Animatable
örneği) oluşturulur ve çağrı sitesinde hatırlanır. İlk hedef değer, 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.
Varsayılan olarak Compose, Float
, Color
, Dp
, Size
, Offset
, Rect
, Int
, IntOffset
ve IntSize
için animate*AsState
işlevleri sağlar. Genel bir tür alan TwoWayConverter
to animateValueAsState
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ğ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ü olabilir. Çoğu durumda, tür güvenliğini doğrulamak için özel bir enum
tür kullanabilirsiniz. Örneğin:
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. 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
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 } }
Bir geçiş hedef duruma ulaştığında Transition.currentState
, Transition.targetState
ile aynıdır. Bu bilgiyi, geçişin tamamlanıp tamamlanmadığını belirlemek için sinyal olarak kullanabilirsiniz.
Bazen ilk hedef durumdan farklı bir başlangıç durumu kullanmak isteyebilirsiniz. Bu amaçla updateTransition
ile MutableTransitionState
ürününü kullanabilirsiniz. Örneğin, kod kompozisyona 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şlevin yer aldığı daha karmaşık bir geçiş için alt geçiş oluşturmak üzere createChildTransition
kullanabilirsiniz. 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ındadır.
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
'in uzantı işlevleri olarak kullanılabilir. targetState
, Transition.AnimatedVisibility
ve Transition.AnimatedContent
için Transition
'den türetilir ve Transition
'ün targetState
değeri değiştiğinde gerektiği gibi giriş, çıkış ve sizeTransform
animasyonlarını tetikler. Bu uzantı işlevleri, normalde AnimatedVisibility
/AnimatedContent
içinde yer alacak 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. 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
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ında, geçiş animasyonlarını kullanıcı arayüzünüzle aynı composable'da tanımlamak geçerli bir seçenektir. Ancak, bir dizi animasyonlu değer içeren 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 içeren 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ı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 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
öğesinin rememberInfiniteTransition
ile bir örneğini oluşturabilir ve animateColor
, animatedFloat
veya animatedValue
ile alt animasyonlar ekleyebilirsiniz. Animasyon özelliklerini belirtmek için bir infiniteRepeatable
de 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 üst düzey animasyon API'lerinin tümü, düşük düzeyli animasyon API'leri üzerine kurulmuştur.
animate*AsState
işlevleri, anlık değer değişikliğini animasyon değeri olarak işleyen basit API'lerdir. Bu işlev, tek bir değeri canlandırmak için kullanılan, eş yordama dayalı bir API olan
Animatable
tarafından desteklenir.
updateTransition
, birden fazla animasyon değerini yönetebilen ve 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
dışındaki tüm bu API'ler composable'dır. Bu nedenle, bu animasyonları composable dışında oluşturabilirsiniz.
Bu API'lerin tümü daha temel olan Animation
API'sine dayanmaktadır. Çoğu uygulama doğrudan Animation
ile etkileşime girmese de bazı özelleştirme özelliklerine daha üst düzey API'ler aracılığıyla 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ğerli animasyon
Animatable
, animateTo
kullanılarak değiştirilen değeri canlandırabilen bir değer tutucudur. Bu, animate*AsState
uygulamasını destekleyen API'dir. Bu, tutarlı devamlılık ve karşılıklı dışlama sağlar. Yani değer değişikliği her zaman kesintisizdir ve Compose, devam eden tüm animasyonları iptal eder.
Animatable
'daki animateTo
dahil birçok özellik askıya alınmış işlevlerdir.
Bu nedenle, bunları uygun bir coroutine kapsamına sarmalamanız gerekir. Örneğin, LaunchedEffect
composable'ı kullanarak yalnızca belirtilen anahtar değerinin süresi için 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) )
Önceki örnekte, Animatable
örneğini Color.Gray
başlangıç değeriyle oluşturup hatırlıyorsunuz. Boole işaretinin ok
değerine bağlı olarak renk, Color.Green
veya Color.Red
olarak animasyon yapar. Boole değerinde yapılan sonraki değişiklikler, diğer renge yönelik bir animasyonu başlatır.
Değer değiştiğinde bir animasyon devam ediyorsa Compose animasyonu iptal eder ve yeni animasyon, mevcut anlık görüntü değeriyle mevcut hızda başlar.
Bu Animatable
API, önceki bölümde bahsedilen animate*AsState
için temel uygulamadır. Animatable
doğrudan kullanmak, çeşitli şekillerde daha ayrıntılı kontrol sağlar:
- İlk olarak,
Animatable
ilk 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ı renkte animasyonla gösterilir. - İkincisi,
Animatable
, içerik değeri üzerinde daha fazla işlem sağlar. ÖzelliklesnapTo
veanimateDecay
.snapTo
, mevcut değeri hemen hedef değere ayarlar. Bu, animasyon tek doğru kaynağı 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, 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
'yi 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
, kullanılabilen en düşük düzeyli Animation API'sidir. Şu ana kadar gördüğümüz animasyonların çoğu Animation
üzerine kuruludur. İki Animation
alt türü vardır: TargetBasedAnimation
ve DecayAnimation
.
Animasyonun süresini manuel olarak kontrol etmek için yalnızca Animation
simgesini kullanın. Animation
durum bilgisi içermez ve yaşam döngüsü kavramı yoktur. Daha üst düzey API'ler için 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 animasyonun oynatma süresini kontrol edebilirsiniz. Aşağıdaki örnekte, TargetAnimation
öğesinin oynatma süresini withFrameNanos
tarafından sağlanan kare süresine göre manuel olarak kontrol ediyorsunuz.
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
'dan farklı olarak DecayAnimation
için targetValue
sağlanması gerekmez. Bunun yerine, targetValue
değerini initialVelocity
ve initialValue
tarafından belirlenen başlangıç koşullarına ve sağlanan DecayAnimationSpec
değerine göre hesaplar.
Çürüme animasyonları, öğeleri durdurmak için genellikle fırlatma hareketinden sonra kullanılır. Animasyon hızı, initialVelocityVector
ile belirlenen değerde başlar ve zamanla yavaşlar.
Sizin için önerilenler
- Not: Bağlantı metni, JavaScript kapalıyken gösterilir.
- Animasyonları özelleştirme
- Compose'da animasyonlar
- Animasyon değiştiriciler ve composable'lar