Kullanıcıların dikkatini çeken, kullanıcıyı yönlendiren ve projeye işlevsellik katan animasyonların tasarımda kullanılması proje talebinin artmasında fayda sağlar.
Bu makale Jetpack Compose tasarım yapısında Animatable ve Animate*AsState Api’lerini kullanarak farklı animasyonları oluşturmayı örnekleyecektir.
Örnekleyeceğim Animasyon API konuları:
- Animatable
- Animate*AsState
- AnimateDpAsState
- AnimateFloatAsState
Animatable fonksiyonunu, diğer tüm animasyon api’lerinden ayıran en önemli özelliği
, compose fonksiyonlarının dışında bir alanda kullanabiliyor olmanızdır. Color ve Float tipi değerleri alan coroutine-based API’dir.
Örneğimizde Animatable fonksiyonuna gri rengi atadığımdan, uygulama ilk açıldığında gri renginden kırmızıya geçiş olacaktır. Sonrasından butona her tıkladığında animasyon çalışacak ve belirli zaman aralığıyla yeşil/kırmızı renge geçiş olacak.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.Animatable import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.* import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.smality.animationswithcompose.ui.theme.AnimationsWithComposeTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AnimationsWithComposeTheme { AnimatableSample() } } } } @Composable private fun AnimatableSample() { var isAnimated by remember { mutableStateOf(false) } //Animatable, gri rengi default olarak atayalım. val color = remember { Animatable(Color.DarkGray) } //LaunchEffect()'te, 'isAnimated' değerine göre rengi değiştirme koşulunu veriyoruz. Butona bastığınızda 'isAnimated' değeri değişir ve LaunchEffect() rengi değiştirmek için tetiklenir. LaunchedEffect(isAnimated) { color.animateTo(if (isAnimated) Color.Green else Color.Red, animationSpec = tween(2000)) } Column(Modifier.fillMaxWidth().absolutePadding(10.dp, 20.dp, 10.dp, 0.dp), horizontalAlignment = Alignment.CenterHorizontally) { Box( Modifier .fillMaxWidth() .fillMaxHeight(0.8f) .background(color.value) ) Button( onClick = { isAnimated = !isAnimated }, ) { Text(text = "Animate Color") } } } |
Tek tipte bir değere animasyon özelliği katmak için kullanılır. Bu değerler Dp, Color, Float, Integer, Offset, Rect…
a) AnimateDpAsState
AnimateDpAsState, state objesi döndürür. Bu objenin değeri animasyon bitene kadar sürekli güncellenir. Bu özellik çalışırken animasyonu durduramayız/iptal edemeyiz.
Örnek de kırmızı kutuya tıkladığımızda boyutunun büyümesini, tekrar tıkladığımızda kutunun küçüldüğünü göreceksiniz.
Kodlarımız:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.Animatable import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.* import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.smality.animationswithcompose.ui.theme.AnimationsWithComposeTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AnimationsWithComposeTheme { AnimateDpExam() } } } } @Composable fun AnimateDpExam() { var sizeDp by remember { mutableStateOf(36.dp) } //Nesnenin default boyutunu ve nasıl bir zamanlama ile animasyonun çalısacağını belirtiyoruz val animateSize by animateDpAsState( targetValue = sizeDp, animationSpec = tween(durationMillis = 2000) ) Box( modifier = Modifier .size(size = animateSize) .background(color = Color.Red) .clickable { //Default değerine eşitse 64 dp yaparak büyüt değilse 36.dp kadar küçült sizeDp = if (sizeDp == 36.dp) 64.dp else 36.dp } ) { } } |
b) AnimateFloatAsState
AnimateDpAsState ile benzer yapıya sahiptir. AnimateFloatAsState API, sadece hedef değer olarak float tipinde değer alır. Örneğimizde butona tıkanıldığında resim açısını animateFloatAsState() kullanarak 0’dan 360’a kadar döndürüyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
import androidx.compose.foundation.* import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.* import androidx.compose.ui.draw.rotate import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.smality.animationswithcompose.ui.theme.AnimationsWithComposeTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AnimationsWithComposeTheme { AnimateAsFloatContent() } } } } @Composable private fun AnimateAsFloatContent() { var isRotated by rememberSaveable { mutableStateOf(false) } val rotationAngle by animateFloatAsState( targetValue = if (isRotated) 360F else 0f, //2.5 saniye hızıyla dönüş gercekleştirme animationSpec = tween(durationMillis = 2500) ) Column(Modifier.fillMaxWidth().absolutePadding(10.dp, 20.dp, 10.dp, 0.dp),horizontalAlignment = Alignment.CenterHorizontally) { Image( painter = painterResource(id=R.drawable.fan), contentDescription = "fan", modifier = Modifier //Döndürme açısını animateFloatAsState fonksiyonundan alıyoruz .rotate(rotationAngle) .size(150.dp) ) Button( onClick = { isRotated = !isRotated }, modifier = Modifier .padding(top = 50.dp) .width(200.dp) ) { Text(text = "Rotate Fan") } } } |