Jetpack Compose ile Animasyon II
Merhaba bu yazımıda Jetpack Compose animasyonlarından bahsetmeye devam edeceğiz. Bir önceki yazımızda üst düzey animasyonlardan bahsetmiştik ki o yazıya buradan ulaşabilirsin;
Bu yazımıda ise androidin bize sunduğu animasyon dökümanındaki kalan başlıklara değineceğiz. O zaman başlayalım;
Düşük seviye animasyon apileri
Bir önceki yazıda bahsettiğimiz üst düzey animasyon apileri’ nin hepsi şimdi bahsedeceğimiz düşük seviyeli animasyon apilerinin ya da daha temel animasyon methodlarının üzerine kurulmuştur.
Örnek verecek olursak animate*AsState
methodları bir önceki yazıda bahsettiğimiz gibi basit bir değer değişikliğini animasyon olarak dönen method olduğunu biliyoruz. İşte gerçekleşen bu olayAnimatable
apisi tarafından desteklenmektedir.
Ayrıca updateTransition
aynı anda birden çok animasyonu çalıştırmamıza ve tek bir yerden yönetmemize yarayan method olduğundan bahsetmiştik. rememberInfiniteTransition
da benzer bir şekilde birden çok animasyonu aynı anda çalıştırabilir ancak updateTransition
dan farkı animasyonlar sonsuz bir şekilde tekrar eder demiştik.
Bu üç animasyon methodlarının, yani üst düzey animasyon apilerinin ortak yanı sadece composable methodlar içinde çalışmalarıdır. Composable methodlar dışında oluşturulamazlar. Ancak düşük seviyeli animasyon apileri composable methodlar dışında da oluşturulabildikleri için daha avantajlıdırlar.
Bütün bu animasyon apilerinin daha anlaşılır olması açısından aşağıdaki şemayı inceleyebilirsin;
Şemada da görüldüğü üzere üst düzey ya da alt seviye animasyon apilerinin hepsi Animation
apisini temel alır. Çoğu modern uygulama doğrudan Animation
apisi ile etkileşim içinde olmasa da görüldüğü üzere daha üst seviye apiler sayesinde oluşturulan özelleştirmeleri kullanmaktadırlar. Animasyonların özelleştirmeleri hakkında daha fazla bilgi almak istiyorsan yazının devamındaki Animasyonları Özelleştirme başlığına bakabilirsin.
Animatable
Animatable
bir değer tutan ve değerin değişmesi durumunu anime eden bir api dir. Bunun için animateTo
methodu kullanılır. Yukarıda da bahsettiğimiz gibi animate*AsState
methodu bu apiyi temel almaktadır.
Animatable
apisinin animateTo
methodu dahil bir çok methodu suspend method olarak tanımlanmıştır. Bundan dolayı herhangi bir Animatable
methodu kullanmak istediğimizde bir coroutineScope
çalıştırmamız gerekmektedir. Aşağıdaki örnekteki gibi LaunchedEffect
ile bir animasyonu tetikleyebiliriz.
Yukarıdaki örnekte görüldüğü gibi Animatable remember
içinde bir Animatable
instance’ i oluşturuyor ve başlangıç değeri tanımlıyoruz. ok değeri true olduğu durumda rengi yeşil, false olduğu durumda ise kırmızı renge anime ediyoruz.
Dipnot: Mevcut instance içinde animasyon henüz tamamlanmamışken yeni bir animasyon çalıştırılmaya kalkışılırsa devam eden animasyon durdurulur ve yeni animasyon başlatılır.
Dipnot:
Animatable
Float
veColor
tiplerini de desteklemektedir. AncakTwoWayConverter
ile diğer veri tiplerinde de animasyon oluşturabiliriz.
Animation
Animation
mevcutta bulunan en alt seviye animasyon apisidir. Şu ana kadar gördüğümüz bütün animasyon apileri Animation
apisini temel almaktadır. Animation
‘ un TargetBasedAnimation
ve DecayAnimation
olmak üzere iki alt tipi vardır;
TargetBasedAnimation:
Diğer apiler bir çok alanda ihtiyacımızı karşılar, ancak TargetBasedAnimation
‘ ı doğrudan kullanmak, animasyon oynatma süresini kendiniz kontrol etmenize olanak tanır. Aşağıdaki örnekte, TargetAnimation
‘ın oynatma süresi, withFrameNanos
tarafından sağlanan kare süresine göre manuel olarak kontrol edilir.
DecayAnimation:
TargetBasedAnimation
‘ dan farklı olarak DecayAnimation
targetValue
değerinin tanımlanmasını zorunlu kılmaz. Onun yerine InitialVelocity
ve InitialValue
ile sağlanan ve DecayAnimationSpec
tarafından ayarlanan başlangıç koşullarına göre kendisi targetValue
değerini hesaplar.
Not:
DecayAnimation
genellikle bir yavaşlatma ve ardından durdurma animasyonları için kullanılır.
Animasyonları Özelleştirme
Çoğu animasyon apisi özelleştirme parametrelerinin kullanılmasını destekler. Hadi bu özelleştirme parametrelerine yakından bakalım;
AnimationSpec
Çoğu animasyon apisi geliştiricinin animasyonu özelleştirmesine izin verir ve bunun için opsiyonel olananimationSpec
parametresini kabul eder.
val alpha: Float by animateFloatAsState(
targetValue = if (enabled) 1f else 0.5f,
// Configure the animation duration and easing.
animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing)
)
Farklı türde animasyon oluşturmamıza olanak sağlaması için çok farklı tipde AnimationSpec
vardır;
spring:
spring
başlangıç ve bitiş değerleri arasında fizik tabanlı bir animasyon oluşturmaya yarar. İki parametre ile çalışır: dampingRatio
ve stiffnes
. dampingRatio
ne kadar zıplanmalı bunu belirler. Varsayılan değeri ise Spring.DampingRatioNoBouncy
‘ dir. Diğer değerleri aşağıda görebilirsin;
stiffnes
ise başlangıç ve bitiş değerleri arasında ne kadar hızlı hareket etmeli onu belirler. Varsayılan değeri ise Stiffnes.StiffnessMedium
‘ dur.
Bilgilendirme:
stiffnes
diğerAnimationSpec
‘ ler içinde en akıcı şekilde animasyonu devam eden özelleştirmedir diyebiliriz. Bundan dolayıanimate*AsState
veupdateTransition
gibi bir çok üst düzey animasyon apisinde kullanılmıştır.
twen:
twen
de spring
gibi başlangıç ve bitiş değerleri arasında animasyon oluşturmaya yararlar. Aralarındaki fark twen
kullanırken durationMilis
değeri vererek animasyon süresini belirtebilir, delayMilis
kullanarak animasyonu geciktirebiliriz. Ayrıca yazının devamında bahsedeceğimiz easing
parametresi alarak da animasyon doğrultu hızını belirtebiliriz.
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = tween(
durationMillis = 300,
delayMillis = 50,
easing = LinearOutSlowInEasing
)
)
keyframes:
keyframes
farklı kare aralıklarına farklı easing
yöntemleri tanımlamamıza yarayan bir methoddur.
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = keyframes {
durationMillis = 375
0.0f at 0 with LinearOutSlowInEasing // for 0-15 ms
0.2f at 15 with FastOutLinearInEasing // for 15-75 ms
0.4f at 75 // ms
0.4f at 225 // ms
}
)
Yukarıdaki örnekte görüldüğü üzere targetValue
değerini istediğimiz kadar parçalara bölüyoruz. keyframes methodu tanımladıktan sonra başlangıç değerinden targetValue
değerine ne kadar sürede ulaşacağını durationMilis
ile belirliyoruz. Ardından bölmek istediğimiz kadar frame aralığı belirliyoruz ve kullanmak istediğimiz easing
yöntemlerini tanımlıyoruz.
Dipnot: Animasyonun düzgün çalışması için burada dikkat edilmesi gereken şey bölüğümüz parçalardaki value değerlerinin toplamı
targetValue
eşit olması gerekmektedir
repeatable:
repeatable
twen
ve keyframes
gibi animasyonları çalıştırabilen ve tekrarlayabilen bir methoddur. iterations
parametresi ile kaç defa tekrar edeceğini, animation
ile hangi animasyonu çalıştıracağımızı, repeatMode
ile ise tekrar türünü belirleyebiliriz;
RepeatMode.Restart
animasyonu en baştan tekrar ederRepeatMode.Reverse
animasyonu bir düz bir ters şekilde tekrar eder
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = repeatable(
iterations = 3,
animation = tween(durationMillis = 300),
repeatMode = RepeatMode.Reverse
)
)
infiniteRepeatable:
infiniteRepeatable
adından da anlaşılacağı üzere repeatable
gibi ancak sonsuz şekilde animasyonu devam ettirir.
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 300),
repeatMode = RepeatMode.Reverse
)
)
snap:
snap
başlangıç değerini hedef değere aniden geçiren bir animasyon yöntemidir. delayMilis
parametresi ile ne kadar süre sonra animasyonun gerçekleşeceğini belirleyebiliriz.
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = snap(delayMillis = 50)
)
Easing
easing
, twen
ya da keyframes
gibi süre bazlı animasyonlarda animasyonun sabit bir hızda hareket etmesi yerine, hızlanmasına ya da yavaşlamasına olanak tanır. easing
aslında 0 ile 1.0 float değerleri arasında bir kesir değeri alan ve kayan bir noktayı döndüren işlevdir. Bu nokta aşmayı ve altında kalmayı temsil etmek için sınırın dışında olabilir.
val CustomEasing = Easing { fraction -> fraction * fraction }
@Composable
fun EasingUsage() {
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = tween(
durationMillis = 300,
easing = CustomEasing
)
)
}
Jetpack compose işimizi kolaylaştırmak açısından bir çok easing
methodunu bizim için oluşturmuş. Sende senin için en uygun easing methodunu buraya tıklayarak seçebilirsin.
AnimationVector
Çoğu Compose animasyon apileri Float
, Color
, Dp
ve diğer basit data tiplerini destekler. Ama bazen farklı bir data tipinde animasyon oluşturmamız gerekebilir. Animasyon süresince herhangi bir animasyon değeri AnimationVector
olarak temsil edilir. Değer TwoWayConverter
tarafından bir AnimationVector
‘ e dönüştürülür. Bu sayede Çekirdek animasyon sistemi değeri düzgün bir şekilde işleyebilir.
Örneğin Int
tipi tek bir float değeri tutan AnimationVector1D
olarak temsil edilir. Int
için TwoWatConverter
kullanımı örneğini aşağıda görebilirsin;
val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })
Bir başka örnek Color
(Red, Green, Blue ve Alpha) olarak dört değerden oluşan bir kümedir. Yani Color
4 float değeri tutan AnimationVector4D
‘ e dönüştürülür.
Buradan şunu diyebiliriz ki animasyonlarda kullanılan her veri türü, boyutuna bağlı olarak AnimationVector1D
, AnimationVector2D
, AnimationVector3D
veyaAnimationVector4D
tipine dönüştürülürler.
Yine bu durumlar için Animation
, temel veri tipleri için Color.VectorConverter
, Dp.VectorConverter
gibi dönüştürücüleri bize sunmaktadır.
Hadi bir de farklı bir data tipini TwoWayConverter
ile animasyon değerine nasıl dönüştürebiliriz aşağıdaki örnekte bakalım;
Yukarıda görüldüğü üzere dp değerlerini float animasyon değerlerine dönüştürerek animasyonumuzu oluşturabiliyoruz.
Kullanıcı Hareketi ve Animasyon
Burada kullanıcı hareketinden kastımız kullanıcının ekranda yaptığı herhangi bir dokunma eylemine göre animasyon oluşturmamızdır. Aşağıda bahsettiğimiz örnekte pointerInput
methodu ile kullanıcın dokunmuş olduğu pozisyonu Offset
tipinde alabilir. Ardından yukarıda öğrendiğimiz çok basit Animatable
yöntemi ile istediğimiz bir viewin offsetini değiştirebiliriz.
Basit bir Gesture Animation
örneğini anlatmış olduk. SwipeToDismiss
gibi daha karmaşık Gesture Animation
örnekleri için buraya tıklayabilirsin.
Bu yazımızda düşük seviyeli animasyon apilerinden ve daha karmaşık animasyonların nasıl oluşturulabileceğinden bahsettik. Umarım kendi animasyon geliştirmende yardımcı olur.
Okuduğun için teşekkür ederim, bir sonraki yazıda görüşmek üzere.
Kaynakça: