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 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 başlatır.
Aşağıda, bu API'nin kullanıldığı bir alfa animasyonu örneği verilmiştir. Hedef değeri animateFloatAsState
içinde sarmaladığınızda alfa değeri artık sağlanan değerler (bu örnekte 1f
veya 0.5f
) arasında bir animasyon değeridir.
var enabled by remember { mutableStateOf(true) } val alpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f) Box( Modifier.fillMaxSize() .graphicsLayer(alpha = alpha) .background(Color.Red) )
Herhangi bir animasyon sınıfının örneğini oluşturmanız veya kesintileri işlemeniz gerekmediğini unutmayın. Gelişmiş görünümde, ilk hedef değeri başlangıç değeri olacak şekilde bir animasyon nesnesi (yani bir Animatable
örneği) oluşturulur ve çağrı sitesinde hatırlanır. Daha sonra, bu composable'a farklı bir hedef değer sağladığınızda, otomatik olarak bu değere doğru bir animasyon başlatılır. Yayın sırası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 composable yeniden derlenir ve her karede güncellenmiş bir animasyon değeri döndürür.
Oluşturulma özelliği, kullanıma hazır olarak Float
, Color
, Dp
, Size
, Offset
, Rect
, Int
, IntOffset
veIntSize
için animate*AsState
işlevleri sağlar. animateValueAsState
için genel türde bir TwoWayConverter
sağlayarak diğer veri türleri için kolayca destek ekleyebilirsiniz.
Bir AnimationSpec
sağlayarak animasyon özelliklerini özelleştirebilirsiniz.
Daha fazla bilgi için AnimationSpec konusuna bakın.
Geçişle birden fazla mülkü aynı anda canlandırma
Transition
, bir veya daha fazla animasyonu alt öğeleri olarak yönetir ve bunları aynı anda birden fazla durum arasında çalıştırır.
Durumlar herhangi bir veri türünde olabilir. Birçok durumda, aşağıdaki örnekte olduğu gibi tür güvenliğini sağlamak için özel bir enum
türü kullanabilirsiniz:
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. 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şikliği kombinasyonlarının her biri 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 } }
Hedef duruma bir geçiş geldiğinde Transition.currentState
, Transition.targetState
ile aynı olur. Bu, geçişin tamamlanıp tamamlanmadığının sinyali olarak kullanılabilir.
Bazen ilk hedef durumdan farklı bir başlangıç durumuna sahip olmak isteriz. Bunun için MutableTransitionState
ile updateTransition
kullanabiliriz. Örneğin, kod besteye 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 = updateTransition(currentState, label = "box state") // ……
Birden fazla composable işlev içeren daha karmaşık bir geçiş için alt öğe geçişi oluşturmak üzere createChildTransition
öğesini kullanabilirsiniz. Bu teknik, karmaşık bir composable'ın birden fazla alt bileşeni arasındaki kaygıları ayırmak için faydalı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ş kullanın
AnimatedVisibility
ve AnimatedContent
, Transition
öğesinin uzantı işlevleri olarak kullanılabilir. Transition.AnimatedVisibility
ve Transition.AnimatedContent
için targetState
, Transition
parametresinden 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, normalde AnimatedVisibility
/AnimatedContent
içinde olacak tüm enter/exit/sizeTransform animasyonlarının Transition
içine taşınmasına olanak tanır.
Bu uzantı işlevleriyle AnimatedVisibility
/AnimatedContent
öğesinin durum değişikliği dışarıdan gözlemlenebilir. Boole visible
parametresi yerine AnimatedVisibility
bu sürümü, üst geçişin hedef durumunu boole'ye dönüştüren bir lambda kullanır.
Ayrıntılar için Animated visibility ve AnimatedContent konuları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), elevation = 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 yeniden kullanılabilir hale getirin
Basit kullanım örneklerinde, kullanıcı arayüzünüzle aynı composable'da geçiş animasyonlarını tanımlamak son derece 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 barındıran bir sınıf ve söz konusu sınıfın örneğini döndüren bir "güncelleme" işlevi oluşturarak yapabilirsiniz. Geçiş uygulaması, yeni ve ayrı bir işleve çıkarılabilir. Bu kalıp, animasyon mantığını merkezileştirme veya karmaşık animasyonları yeniden kullanılabilir hale getirme 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 sayıda tekrarlanan animasyon oluşturun
InfiniteTransition
, Transition
gibi bir veya daha fazla alt animasyon içerir ancak animasyonlar besteye girer girmez çalışmaya başlar ve kaldırılmadıkça durmaz. rememberInfiniteTransition
ile InfiniteTransition
örneği oluşturabilirsiniz. Alt animasyonlar animateColor
, animatedFloat
veya animatedValue
ile eklenebilir. Animasyon özelliklerini belirtmek için bir infiniteRepeatable de belirtmeniz gerekir.
val infiniteTransition = rememberInfiniteTransition() val color by infiniteTransition.animateColor( initialValue = Color.Red, targetValue = Color.Green, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ) ) Box(Modifier.fillMaxSize().background(color))
Alt düzey animasyon API'leri
Önceki bölümde bahsedilen üst düzey animasyon API'lerinin tümü, düşük seviyeli animasyon API'lerinin temeli üzerine inşa edilmiştir.
animate*AsState
işlevleri, anında değer değişikliğini animasyon değeri olarak oluşturan en basit API'lerdir. Tek bir değeri canlandırmaya yönelik eş yordam tabanlı bir API olan Animatable
tarafından desteklenir. updateTransition
, birden fazla animasyon değerini 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 çok animasyonu yönetebilen sonsuz bir geçiş oluşturur. Bu API'lerin tümü composable'dır. Animatable
dışında, bu animasyonlar kompozisyon dışında oluşturulabilir.
Bu API'lerin tümü, daha temel bir API olan Animation
API'sini temel alır. Çoğu uygulama Animation
ile doğrudan etkileşim kurmasa da Animation
için bazı özelleştirme işlevleri, üst düzey API'ler aracılığıyla kullanılabilir. AnimationVector
ve AnimationSpec
hakkında daha fazla bilgi için Animasyonları özelleştirme'ye bakın.
Animatable
: Eşliğe dayalı tek değer animasyonu
Animatable
, animateTo
aracılığıyla değiştirilen değeri canlandırabilen bir değer sahibidir. Bu, animate*AsState
uygulamasını yedekleyen API'dir.
Tutarlı bir devamı ve karşılıklı dışlayıcılığı sağlar, yani değer değişikliği her zaman sürekli olur ve devam eden tüm animasyonlar iptal edilir.
animateTo
dahil olmak üzere Animatable
, askıya alma işlevleri olarak sağlanır. Yani uygun bir eş yordam kapsamına alınması gerekir. Örneğin, yalnızca belirtilen anahtar değerinin süresi boyunca bir kapsam oluşturmak için LaunchedEffect
composable'ı 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, Color.Gray
başlangıç değerine sahip bir Animatable
örneği oluşturup hatırlıyoruz. Boole bayrağının değerine (ok
) bağlı olarak, renk Color.Green
veya Color.Red
olarak canlandırılır. Boole değerinde daha sonra yapılacak herhangi bir değişiklik, animasyonu diğer renkle başlatır. Değer değiştirildiğinde devam eden bir animasyon varsa animasyon iptal edilir ve yeni animasyon geçerli hızda 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
doğrudan kullanıldığında birçok konuda bize daha hassas kontrol olanağı sunuluyor. Öncelikle,
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österilmektedir. Bu kutu hemen yeşil veya kırmızı animasyona 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, animasyonun tek doğru kaynağı olmadığı ve dokunma etkinlikleri gibi diğer durumlarla senkronize edilmesi gerektiğinde yararlı olur. animateDecay
, belirtilen hızdan yavaşlayan bir animasyon başlatır. Bu, kaçma davranışının uygulanmasında yararlıdır. Daha fazla bilgi için Hareket ve animasyon bölümüne bakın.
Animatable
, hemen Float
ve Color
özelliklerini destekler. Ancak bir TwoWayConverter
sağlanarak tüm veri türleri kullanılabilir. Daha fazla bilgi için bkz. AnimationVector.
Bir AnimationSpec
sağlayarak animasyon özelliklerini özelleştirebilirsiniz.
Daha fazla bilgi için AnimationSpec konusuna bakın.
Animation
: Manuel olarak kontrol edilen animasyon
Animation
, mevcut en düşük seviyeli Animasyon API'sidir. Şu ana kadar gördüğümüz animasyonların çoğu,
Animasyon'un üzerine inşa edilmiştir. İki Animation
alt türü vardır:
TargetBasedAnimation
ve DecayAnimation
.
Animation
, yalnızca animasyonun zamanını manuel olarak kontrol etmek için kullanılmalıdır.
Animation
durum bilgisizdir ve yaşam döngüsü kavramı yoktur. Daha ü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 oynatılma süresi, withFrameNanos
tarafından sağlanan kare süresine göre manuel olarak kontrol edilmektedir.
val anim = remember { TargetBasedAnimation( animationSpec = tween(200), typeConverter = Float.VectorConverter, initialValue = 200f, targetValue = 1000f ) } var playTime by remember { mutableStateOf(0L) } LaunchedEffect(anim) { val startTime = withFrameNanos { it } do { playTime = withFrameNanos { it } - startTime val animationValue = anim.getValueFromNanos(playTime) } while (someCustomCondition()) }
DecayAnimation
TargetBasedAnimation
'in aksine, DecayAnimation
için targetValue
sağlanması gerekmez. Bunun yerine, targetValue
değerini initialVelocity
ile initialValue
ve sağlanan DecayAnimationSpec
tarafından ayarlanan başlangıç koşullarına göre hesaplar.
Azaltma animasyonları, genellikle öğeleri bir duraklamaya kadar yavaşlatmak için bir sallama hareketinden sonra kullanılır. Animasyon hızı, initialVelocityVector
tarafından ayarlanan değerden başlar ve zamanla yavaşlar.
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken görüntülenir
- Animasyonları özelleştirme {:#customize-animations}
- Oluşturma bölümünde animasyonlar
- Animasyon değiştiricileri ve composable'lar