Oluşturma'daki Animasyonlar için hızlı rehber

Oluşturma işleminde birçok yerleşik animasyon mekanizması vardır ve hangisini seçeceğinizi bilmek zor olabilir. Aşağıda, animasyonların yaygın kullanım alanlarını görebilirsiniz. Kullanabileceğiniz farklı API seçeneklerinin tamamı hakkında daha ayrıntılı bilgi için Animasyon Oluşturma dokümanının tamamını okuyun.

Ortak, birleştirilebilir özellikleri canlandırma

Compose, birçok yaygın animasyon kullanım alanını çözmenize olanak tanıyan kullanışlı API'ler sağlar. Bu bölümde, bir bileşimin ortak özelliklerini nasıl canlandırabileceğiniz gösterilmektedir.

Görünme / kaybolma animasyonları

Kendisini gösteren ve gizleyen yeşil bir bileşimli öğe
Şekil 1. Bir sütundaki öğenin görünmesini ve kaybolmasını animasyonlu hale getirme

Bir bileşeni gizlemek veya göstermek için AnimatedVisibility simgesini kullanın. AnimatedVisibility içindeki çocuklar, kendi giriş veya çıkış geçişleri için Modifier.animateEnterExit() öğesini kullanabilir.

var visible by remember {
    mutableStateOf(true)
}
// Animated visibility will eventually remove the item from the composition once the animation has finished.
AnimatedVisibility(visible) {
    // your composable here
    // ...
}

AnimatedVisibility'ün giriş ve çıkış parametreleri, bir composable'ın göründüğünde ve kaybolduğunda nasıl davranacağını yapılandırmanıza olanak tanır. Daha fazla bilgi için tam dokümanları okuyun.

Bir bileşiğin görünürlüğünü animasyonlu hale getirmenin bir başka yolu da animateFloatAsState kullanarak alfa değerini zaman içinde animasyonlu hale getirmektir:

var visible by remember {
    mutableStateOf(true)
}
val animatedAlpha by animateFloatAsState(
    targetValue = if (visible) 1.0f else 0f,
    label = "alpha"
)
Box(
    modifier = Modifier
        .size(200.dp)
        .graphicsLayer {
            alpha = animatedAlpha
        }
        .clip(RoundedCornerShape(8.dp))
        .background(colorGreen)
        .align(Alignment.TopCenter)
) {
}

Ancak alfa değerini değiştirmek, derlenebilir öğenin bileşimde kalacağı ve yerleştirildiği alanı kaplamaya devam edeceği anlamına gelir. Bu durum, ekran okuyucuların ve diğer erişilebilirlik mekanizmalarının öğeyi ekranda olarak değerlendirmeye devam etmesine neden olabilir. Öte yandan AnimatedVisibility, öğeyi nihayetinde kompozisyondan kaldırır.

Bir bileşenin alfa değerini animasyonlu hale getirme
Şekil 2. Bir bileşimin alfa değerini animasyonlu hale getirme

Arka plan rengini animasyonlu hale getirme

Arka plan renginin zaman içinde animasyon olarak değiştiği ve renklerin birbirine karıştığı bir kompozisyon.
Şekil 3. Birleştirilebilir öğenin arka plan rengini animasyonlu hale getirme

val animatedColor by animateColorAsState(
    if (animateBackgroundColor) colorGreen else colorBlue,
    label = "color"
)
Column(
    modifier = Modifier.drawBehind {
        drawRect(animatedColor)
    }
) {
    // your composable here
}

Bu seçenek, Modifier.background() kullanmaktan daha yüksek performanslıdır. Modifier.background(), tek seferlik bir renk ayarı için kabul edilebilir ancak bir rengin zaman içinde animasyonu yapılırken bu, gerekenden daha fazla yeniden oluşturmaya neden olabilir.

Arka plan rengini sonsuz olarak animasyonlu hale getirmek için animasyonu tekrarlama bölümüne bakın.

Bir bileşenin boyutunu animasyonlu olarak değiştirme

Boyutunu sorunsuz bir şekilde değiştiren yeşil bir kompozisyon.
Şekil 4. Küçük ve daha büyük bir boyut arasında sorunsuz bir şekilde animasyonlu olarak derlenebilir

Oluşturma, bileşenlerin boyutunu birkaç farklı şekilde canlandırmanıza olanak tanır. Birleştirilebilir boyut değişiklikleri arasındaki animasyonlar için animateContentSize() kullanın.

Örneğin, bir satırdan birden fazla satıra kadar genişleyebilecek metin içeren bir kutunuz varsa daha yumuşak bir geçiş elde etmek için Modifier.animateContentSize() simgesini kullanabilirsiniz:

var expanded by remember { mutableStateOf(false) }
Box(
    modifier = Modifier
        .background(colorBlue)
        .animateContentSize()
        .height(if (expanded) 400.dp else 200.dp)
        .fillMaxWidth()
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            expanded = !expanded
        }

) {
}

Boyut değişikliklerinin nasıl yapılması gerektiğini açıklamak için SizeTransform ile birlikte AnimatedContent'u da kullanabilirsiniz.

Birleştirilebilir öğenin konumuna animasyon uygulama

Yeşil, aşağı ve sağa doğru sorunsuz bir şekilde animasyonlu olarak derlenen öğe
Şekil 5. Bir ofsetle hareket eden kompozisyonlar

Bir kompozisyonun konumunu animasyonlu hale getirmek için Modifier.offset{ } ile birlikte animateIntOffsetAsState() kullanın.

var moved by remember { mutableStateOf(false) }
val pxToMove = with(LocalDensity.current) {
    100.dp.toPx().roundToInt()
}
val offset by animateIntOffsetAsState(
    targetValue = if (moved) {
        IntOffset(pxToMove, pxToMove)
    } else {
        IntOffset.Zero
    },
    label = "offset"
)

Box(
    modifier = Modifier
        .offset {
            offset
        }
        .background(colorBlue)
        .size(100.dp)
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            moved = !moved
        }
)

Konum veya boyutu animasyonlu olarak değiştirirken bileşenlerin diğer bileşenlerin üzerine veya altına çizilmediğinden emin olmak istiyorsanız Modifier.layout{ } seçeneğini kullanın. Bu değiştirici, boyut ve konum değişikliklerini ebeveyne iletir. Bu da diğer çocukları etkiler.

Örneğin, bir Box öğesini bir Column içinde taşıyorsanız ve Box öğesi taşındığında diğer alt öğelerin de taşınması gerekiyorsa ofset bilgilerini Modifier.layout{ } ile aşağıdaki gibi ekleyin:

var toggled by remember {
    mutableStateOf(false)
}
val interactionSource = remember {
    MutableInteractionSource()
}
Column(
    modifier = Modifier
        .padding(16.dp)
        .fillMaxSize()
        .clickable(indication = null, interactionSource = interactionSource) {
            toggled = !toggled
        }
) {
    val offsetTarget = if (toggled) {
        IntOffset(150, 150)
    } else {
        IntOffset.Zero
    }
    val offset = animateIntOffsetAsState(
        targetValue = offsetTarget, label = "offset"
    )
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(colorBlue)
    )
    Box(
        modifier = Modifier
            .layout { measurable, constraints ->
                val offsetValue = if (isLookingAhead) offsetTarget else offset.value
                val placeable = measurable.measure(constraints)
                layout(placeable.width + offsetValue.x, placeable.height + offsetValue.y) {
                    placeable.placeRelative(offsetValue)
                }
            }
            .size(100.dp)
            .background(colorGreen)
    )
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(colorBlue)
    )
}

2. kutunun X, Y konumunu animasyonlu olarak değiştirdiği 2 kutu ve üçüncü kutunun da Y miktarı kadar hareket ederek yanıt verdiği bir örnek.
Şekil 6. Modifier.layout{ } ile animasyon oluşturma

Bir bileşenin dolgusunu canlandırma

Yeşil kompozisyon, tıklandığında daha küçük ve daha büyük hale gelirken dolgu animasyonlu olur.
Şekil 7. Dolgulu animasyonuyla birlikte derlenebilir

Bir bileşenin dolgusunu animasyonlu hale getirmek için animateDpAsState ile birlikte Modifier.padding()'ü kullanın:

var toggled by remember {
    mutableStateOf(false)
}
val animatedPadding by animateDpAsState(
    if (toggled) {
        0.dp
    } else {
        20.dp
    },
    label = "padding"
)
Box(
    modifier = Modifier
        .aspectRatio(1f)
        .fillMaxSize()
        .padding(animatedPadding)
        .background(Color(0xff53D9A1))
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            toggled = !toggled
        }
)

Bir bileşenin yüksekliğini animasyonlu olarak değiştirme

Şekil 8. Composable'ın tıklandığında yükseklik animasyonu

Bir bileşimin yüksekliğini animasyonlu olarak değiştirmek için animateDpAsState ile birlikte Modifier.graphicsLayer{ } kullanın. Tek seferlik yükseklik değişiklikleri için Modifier.shadow() kullanın. Gölgeyi animasyonlu hale getiriyorsanız Modifier.graphicsLayer{ } değiştiricisini kullanmak daha yüksek performanslı bir seçenektir.

val mutableInteractionSource = remember {
    MutableInteractionSource()
}
val pressed = mutableInteractionSource.collectIsPressedAsState()
val elevation = animateDpAsState(
    targetValue = if (pressed.value) {
        32.dp
    } else {
        8.dp
    },
    label = "elevation"
)
Box(
    modifier = Modifier
        .size(100.dp)
        .align(Alignment.Center)
        .graphicsLayer {
            this.shadowElevation = elevation.value.toPx()
        }
        .clickable(interactionSource = mutableInteractionSource, indication = null) {
        }
        .background(colorGreen)
) {
}

Alternatif olarak, Card bileşimini kullanın ve yükseklik özelliğini eyalet başına farklı değerlere ayarlayın.

Metin ölçeği, hareketi veya döndürülmesi için animasyon ekleme

Metin olarak derlenebilir söz
Şekil 9. Metnin iki boyut arasında sorunsuz bir şekilde animasyonlu olarak gösterilmesi

Metnin ölçeğini, çevirisini veya dönüşünü animasyonlu hale getirirken TextStyle üzerindeki textMotion parametresini TextMotion.Animated olarak ayarlayın. Böylece metin animasyonları arasında daha yumuşak geçişler elde edersiniz. Metni çevirmek, döndürmek veya ölçeklendirmek için Modifier.graphicsLayer{ } simgesini kullanın.

val infiniteTransition = rememberInfiniteTransition(label = "infinite transition")
val scale by infiniteTransition.animateFloat(
    initialValue = 1f,
    targetValue = 8f,
    animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
    label = "scale"
)
Box(modifier = Modifier.fillMaxSize()) {
    Text(
        text = "Hello",
        modifier = Modifier
            .graphicsLayer {
                scaleX = scale
                scaleY = scale
                transformOrigin = TransformOrigin.Center
            }
            .align(Alignment.Center),
        // Text composable does not take TextMotion as a parameter.
        // Provide it via style argument but make sure that we are copying from current theme
        style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated)
    )
}

Metin rengini animasyonlu hale getirme

Kelimeler
Şekil 10. Metin renginin animasyonunu gösteren örnek

Metin rengini animasyonlu hale getirmek için BasicText kompozisyonunda color lambdasını kullanın:

val infiniteTransition = rememberInfiniteTransition(label = "infinite transition")
val animatedColor by infiniteTransition.animateColor(
    initialValue = Color(0xFF60DDAD),
    targetValue = Color(0xFF4285F4),
    animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
    label = "color"
)

BasicText(
    text = "Hello Compose",
    color = {
        animatedColor
    },
    // ...
)

Farklı içerik türleri arasında geçiş yapma

Yeşil ekranda gösterilen mesaj
Şekil 11. Farklı bileşenler arasındaki değişiklikleri animasyonlu hale getirmek için AnimatedContent'i kullanma (yavaşlatılmış)

Farklı kompozisyonlar arasında animasyon oluşturmak için AnimatedContent kullanın. Kompozitörler arasında standart bir solma efekti oluşturmak istiyorsanız Crossfade'ı kullanın.

var state by remember {
    mutableStateOf(UiState.Loading)
}
AnimatedContent(
    state,
    transitionSpec = {
        fadeIn(
            animationSpec = tween(3000)
        ) togetherWith fadeOut(animationSpec = tween(3000))
    },
    modifier = Modifier.clickable(
        interactionSource = remember { MutableInteractionSource() },
        indication = null
    ) {
        state = when (state) {
            UiState.Loading -> UiState.Loaded
            UiState.Loaded -> UiState.Error
            UiState.Error -> UiState.Loading
        }
    },
    label = "Animated Content"
) { targetState ->
    when (targetState) {
        UiState.Loading -> {
            LoadingScreen()
        }
        UiState.Loaded -> {
            LoadedScreen()
        }
        UiState.Error -> {
            ErrorScreen()
        }
    }
}

AnimatedContent, birçok farklı giriş ve çıkış geçişi göstermek için özelleştirilebilir. Daha fazla bilgi için AnimatedContent ile ilgili dokümanları veya AnimatedContent ile ilgili bu blog yayınını okuyun.

Farklı hedeflere giderken animasyon gösterme

Biri yeşil ve "Açılış", diğeri mavi ve "Ayrıntı" yazan iki composable. Ayrıntı composable'ı, açılış composable'ının üzerine kaydırılarak animasyon oluşturulur.
Şekil 12. navigation-compose kullanarak bileşenler arasında animasyon oluşturma

navigation-compose yapısını kullanırken bileşenler arasındaki geçişleri animasyonlu hale getirmek için bir bileşende enterTransition ve exitTransition öğelerini belirtin. Varsayılan animasyonu üst düzeydeki tüm hedefler için kullanılacak şekilde de ayarlayabilirsiniz NavHost:

val navController = rememberNavController()
NavHost(
    navController = navController, startDestination = "landing",
    enterTransition = { EnterTransition.None },
    exitTransition = { ExitTransition.None }
) {
    composable("landing") {
        ScreenLanding(
            // ...
        )
    }
    composable(
        "detail/{photoUrl}",
        arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }),
        enterTransition = {
            fadeIn(
                animationSpec = tween(
                    300, easing = LinearEasing
                )
            ) + slideIntoContainer(
                animationSpec = tween(300, easing = EaseIn),
                towards = AnimatedContentTransitionScope.SlideDirection.Start
            )
        },
        exitTransition = {
            fadeOut(
                animationSpec = tween(
                    300, easing = LinearEasing
                )
            ) + slideOutOfContainer(
                animationSpec = tween(300, easing = EaseOut),
                towards = AnimatedContentTransitionScope.SlideDirection.End
            )
        }
    ) { backStackEntry ->
        ScreenDetails(
            // ...
        )
    }
}

Gelen ve giden içeriğe farklı efektler uygulayan birçok farklı giriş ve çıkış geçişi vardır. Daha fazla bilgi için dokümanlara bakın.

Animasyonu tekrarlama

İki renk arasında animasyonla sonsuz şekilde mavi arka plana dönüşen yeşil arka plan.
Şekil 13. Arka plan renginin iki değer arasında sonsuz şekilde animasyonu

Animasyonunuzu sürekli olarak tekrarlamak için infiniteRepeatable animationSpec ile birlikte rememberInfiniteTransition kullanın. RepeatModes değerini değiştirerek nasıl ileri geri gideceğini belirtin.

Belirli bir sayıda tekrarlamak için finiteRepeatable simgesini kullanın.

val infiniteTransition = rememberInfiniteTransition(label = "infinite")
val color by infiniteTransition.animateColor(
    initialValue = Color.Green,
    targetValue = Color.Blue,
    animationSpec = infiniteRepeatable(
        animation = tween(1000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    ),
    label = "color"
)
Column(
    modifier = Modifier.drawBehind {
        drawRect(color)
    }
) {
    // your composable here
}

Bir bileşenin başlatılmasıyla animasyon başlatma

LaunchedEffect, bir bileşen bileşime girdiğinde çalışır. Bir bileşenin başlatılmasıyla birlikte bir animasyon başlatır. Animasyon durumunu değiştirmek için bunu kullanabilirsiniz. Animasyonu başlatmak için Animatable yöntemini animateTo yöntemiyle birlikte kullanın:

val alphaAnimation = remember {
    Animatable(0f)
}
LaunchedEffect(Unit) {
    alphaAnimation.animateTo(1f)
}
Box(
    modifier = Modifier.graphicsLayer {
        alpha = alphaAnimation.value
    }
)

Sıralı animasyonlar oluşturma

Yeşil oklarla birbirine bağlanmış dört daire, birbiri ardına animasyonlu olarak gösterilir.
Şekil 14. Sıralı bir animasyonun tek tek nasıl ilerlediğini gösteren şema.

Sıralı veya eşzamanlı animasyonlar gerçekleştirmek için Animatable coroutine API'lerini kullanın. Animatable üzerinde animateTo işlevinin art arda çağrılması, her bir animasyonun devam etmeden önce önceki animasyonların tamamlanmasını beklemesine neden olur . Bunun nedeni, askıya alma işlevi olmasıdır.

val alphaAnimation = remember { Animatable(0f) }
val yAnimation = remember { Animatable(0f) }

LaunchedEffect("animationKey") {
    alphaAnimation.animateTo(1f)
    yAnimation.animateTo(100f)
    yAnimation.animateTo(500f, animationSpec = tween(100))
}

Eş zamanlı animasyon oluşturma

Yeşil oklarla animasyonlu üç daire, aynı anda animasyonlu.
Şekil 15. Eşzamanlı animasyonların aynı anda nasıl ilerlediğini gösteren şema.

Eşzamanlı animasyonlar elde etmek için coroutine API'lerini (Animatable#animateTo() veya animate) ya da Transition API'yi kullanın. Bir coroutine bağlamında birden fazla başlatma işlevi kullanırsanız animasyonlar aynı anda başlatılır:

val alphaAnimation = remember { Animatable(0f) }
val yAnimation = remember { Animatable(0f) }

LaunchedEffect("animationKey") {
    launch {
        alphaAnimation.animateTo(1f)
    }
    launch {
        yAnimation.animateTo(100f)
    }
}

Aynı durumu kullanarak aynı anda birçok farklı mülk animasyonu oluşturmak için updateTransition API'yi kullanabilirsiniz. Aşağıdaki örnekte, durum değişikliğiyle kontrol edilen iki mülk (rect ve borderWidth) animasyonlu olarak gösterilmektedir:

var currentState by remember { mutableStateOf(BoxState.Collapsed) }
val transition = updateTransition(currentState, label = "transition")

val rect by transition.animateRect(label = "rect") { state ->
    when (state) {
        BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f)
        BoxState.Expanded -> Rect(100f, 100f, 300f, 300f)
    }
}
val borderWidth by transition.animateDp(label = "borderWidth") { state ->
    when (state) {
        BoxState.Collapsed -> 1.dp
        BoxState.Expanded -> 0.dp
    }
}

Animasyon performansını optimize etme

Oluşturma bölümündeki animasyonlar performans sorunlarına neden olabilir. Bunun nedeni, animasyonların doğası gereği hareket yanılsaması oluşturmak için ekrandaki pikselleri kare kare hızlı bir şekilde hareket ettirmesi veya değiştirmesidir.

Oluşturma'nın farklı aşamalarını göz önünde bulundurun: kompozisyon, düzen ve çizim. Animasyonunuz, düzen aşamasını değiştirirse etkilenen tüm kompozisyonların yeniden düzenlenmesi ve yeniden çizilmesi gerekir. Animasyonunuz çizim aşamasında gerçekleşirse varsayılan olarak, animasyonu düzen aşamasında çalıştırdığınızdan daha yüksek performans gösterir. Bunun nedeni, genel olarak daha az iş yapmasıdır.

Uygulamanızın animasyon sırasında mümkün olduğunca az işlem yapması için mümkün olduğunda Modifier öğesinin lambda sürümünü seçin. Bu, yeniden oluşturma işlemini atlar ve animasyonu oluşturma aşamasının dışında gerçekleştirir. Aksi takdirde, bu değiştirici her zaman çizim aşamasında çalıştığından Modifier.graphicsLayer{ } kullanın. Bu konuda daha fazla bilgi için performans dokümanlarındaki okuma işlemlerini erteleme bölümüne bakın.

Animasyon zamanlamasını değiştirme

Oluşturma işlemi, çoğu animasyon için varsayılan olarak yay animasyonları kullanır. Yaylar veya fiziğe dayalı animasyonlar daha doğaldır. Ayrıca, sabit bir süre yerine nesnenin mevcut hızını hesaba kattıkları için kesintiye uğratılabilirler. Varsayılan ayarı geçersiz kılmak istiyorsanız yukarıda gösterilen tüm animasyon API'lerinde, animasyonların nasıl çalışacağını özelleştirmek için bir animationSpec ayarlayabilirsiniz. Böylece animasyonların belirli bir süre boyunca yürütülmesini veya daha hareketli olmasını sağlayabilirsiniz.

Farklı animationSpec seçeneklerinin özeti aşağıda verilmiştir:

  • spring: Fizik tabanlı animasyon, tüm animasyonlar için varsayılan ayardır. Farklı bir animasyon görünümü ve hissi elde etmek için stiffness veya dampingRatio değerini değiştirebilirsiniz.
  • tween (between kısaltması): Süreye dayalı animasyon. Easing işleviyle iki değer arasında animasyon oluşturur.
  • keyframes: Animasyonun belirli önemli noktalarında değerleri belirtmek için kullanılan özellik.
  • repeatable: RepeatMode tarafından belirtilen belirli sayıda çalıştırılan, süreye dayalı spesifikasyon.
  • infiniteRepeatable: Sonsuza kadar çalışan süreye dayalı spesifikasyon.
  • snap: Animasyon olmadan son değere anında sabitlenir.
Alternatif metninizi buraya yazın
Şekil 16. Özellik kümesi yok ve özel Spring özellik kümesi

animationSpecs hakkında daha fazla bilgi için dokümanların tamamını okuyun.

Ek kaynaklar

Oluşturma'daki eğlenceli animasyonlarla ilgili daha fazla örnek için aşağıdakilere göz atın: