Animasyon değiştiricileri ve kompozit öğeleri

Compose, yaygın animasyon kullanım alanlarını işlemek için yerleşik composable'lar ve değiştiricilerle birlikte gelir.

Yerleşik animasyonlu composable'lar

Compose, içeriğin görünümünü, kaybolmasını ve düzen değişikliklerini canlandıran çeşitli composable'lar sağlar.

Görünme ve kaybolma animasyonu

Yeşil composable'ın gösterilmesi ve gizlenmesi
Şekil 1. Bir sütundaki öğenin görünme ve kaybolma animasyonu.

The AnimatedVisibility composable, içeriğinin görünümünü ve kaybolmasını animasyonla gösterir.

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
    // ...
}

İçerik varsayılan olarak solarak ve genişleyerek görünür, solarak ve küçülerek kaybolur. EnterTransition ve ExitTransition nesnelerini belirterek bu geçişi özelleştirin.

var visible by remember { mutableStateOf(true) }
val density = LocalDensity.current
AnimatedVisibility(
    visible = visible,
    enter = slideInVertically {
        // Slide in from 40 dp from the top.
        with(density) { -40.dp.roundToPx() }
    } + expandVertically(
        // Expand from the top.
        expandFrom = Alignment.Top
    ) + fadeIn(
        // Fade in with the initial alpha of 0.3f.
        initialAlpha = 0.3f
    ),
    exit = slideOutVertically() + shrinkVertically() + fadeOut()
) {
    Text(
        "Hello",
        Modifier
            .fillMaxWidth()
            .height(200.dp)
    )
}

Önceki örnekte gösterildiği gibi, birden fazla EnterTransition veya ExitTransition nesnesini + operatörüyle birleştirebilirsiniz. Her biri, davranışını özelleştirmek için isteğe bağlı parametreleri kabul eder. Daha fazla bilgi için referans sayfalarına bakın.

Giriş ve çıkış geçişi örnekleri

EnterTransition ExitTransition
fadeIn
Bir kullanıcı arayüzü öğesi yavaş yavaş görünür hale geliyor.
fadeOut
Bir kullanıcı arayüzü öğesi yavaş yavaş görünmez hale geliyor.
slideIn
Bir kullanıcı arayüzü öğesi ekran dışından görünüm alanına kayar.
slideOut
Bir kullanıcı arayüzü öğesi, ekranın dışına kayarak görünümden çıkıyor.
slideInHorizontally
Bir kullanıcı arayüzü öğesi yatay olarak görünür hale gelir.
slideOutHorizontally
Bir kullanıcı arayüzü öğesi, görünümden yatay olarak kayar.
slideInVertically
Bir kullanıcı arayüzü öğesi dikey olarak kayarak görünür.
slideOutVertically
Bir kullanıcı arayüzü öğesi dikey olarak görünümden kayar.
scaleIn
Bir kullanıcı arayüzü öğesi büyütülerek görünür hale gelir.
scaleOut
Bir kullanıcı arayüzü öğesi küçülerek görünümden çıkıyor.
expandIn
Bir kullanıcı arayüzü öğesi, merkezi bir noktadan görünümde genişliyor.
shrinkOut
Bir kullanıcı arayüzü öğesi, görünümden çıkarak merkezi bir noktaya doğru küçülür.
expandHorizontally
Bir kullanıcı arayüzü öğesi yatay olarak genişleyerek görünür hale geliyor.
shrinkHorizontally
Bir kullanıcı arayüzü öğesi, görünümün dışına doğru yatay olarak küçülüyor.
expandVertically
Bir kullanıcı arayüzü öğesi dikey olarak genişleyerek görünüme giriyor.
shrinkVertically
Bir kullanıcı arayüzü öğesi dikey olarak küçülerek görünümden kaybolur.

AnimatedVisibility, MutableTransitionState bağımsız değişkenini alan bir varyant da sunar. Bu sayede, AnimatedVisibility composable'ı kompozisyon ağacına eklendiği anda animasyonu tetikleyebilirsiniz. Animasyon durumunu gözlemlemek için de kullanışlıdır.

// Create a MutableTransitionState<Boolean> for the AnimatedVisibility.
val state = remember {
    MutableTransitionState(false).apply {
        // Start the animation immediately.
        targetState = true
    }
}
Column {
    AnimatedVisibility(visibleState = state) {
        Text(text = "Hello, world!")
    }

    // Use the MutableTransitionState to know the current animation state
    // of the AnimatedVisibility.
    Text(
        text = when {
            state.isIdle && state.currentState -> "Visible"
            !state.isIdle && state.currentState -> "Disappearing"
            state.isIdle && !state.currentState -> "Invisible"
            else -> "Appearing"
        }
    )
}

Çocuklar için giriş ve çıkış animasyonları

AnimatedVisibility içindeki içerikler (doğrudan veya dolaylı alt öğeler), her biri için farklı animasyon davranışı belirtmek üzere animateEnterExit değiştiricisini kullanabilir. Bu alt öğelerin her birinin görsel efekti, AnimatedVisibility composable'da belirtilen animasyonların ve alt öğenin kendi giriş ve çıkış animasyonlarının bir kombinasyonudur.

var visible by remember { mutableStateOf(true) }

AnimatedVisibility(
    visible = visible,
    enter = fadeIn(),
    exit = fadeOut()
) {
    // Fade in/out the background and the foreground.
    Box(
        Modifier
            .fillMaxSize()
            .background(Color.DarkGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .animateEnterExit(
                    // Slide in/out the inner box.
                    enter = slideInVertically(),
                    exit = slideOutVertically()
                )
                .sizeIn(minWidth = 256.dp, minHeight = 64.dp)
                .background(Color.Red)
        ) {
            // Content of the notification…
        }
    }
}

Bazı durumlarda, çocukların her birinin animateEnterExit ile kendi animasyonlarını kullanabilmesi için AnimatedVisibility'nın hiç animasyon uygulamamasını isteyebilirsiniz. Bunu yapmak için AnimatedVisibility composable'ında EnterTransition.None ve ExitTransition.None değerlerini belirtin.

Özel animasyon ekleme

Yerleşik giriş ve çıkış animasyonlarının dışında özel animasyon efektleri eklemek istiyorsanız Transition için içerik lambda'sındaki transition özelliğini kullanarak temel AnimatedVisibility örneğine erişin. Geçiş örneğine eklenen tüm animasyon durumları, AnimatedVisibility öğesinin giriş ve çıkış animasyonlarıyla aynı anda çalışır. AnimatedVisibility, içeriğini kaldırmadan önce Transition içindeki tüm animasyonların tamamlanmasını bekler. Transition dışında oluşturulan çıkış animasyonları (ör. animate*AsState kullanılarak oluşturulanlar) için AnimatedVisibility bunları hesaba katamaz ve bu nedenle, tamamlanmadan önce içerik composable'ı kaldırabilir.

var visible by remember { mutableStateOf(true) }

AnimatedVisibility(
    visible = visible,
    enter = fadeIn(),
    exit = fadeOut()
) { // this: AnimatedVisibilityScope
    // Use AnimatedVisibilityScope#transition to add a custom animation
    // to the AnimatedVisibility.
    val background by transition.animateColor(label = "color") { state ->
        if (state == EnterExitState.Visible) Color.Blue else Color.Gray
    }
    Box(
        modifier = Modifier
            .size(128.dp)
            .background(background)
    )
}

Animasyonları yönetmek için Transition simgesini kullanma hakkında daha fazla bilgi edinmek için Geçişle birden fazla özelliği aynı anda canlandırma başlıklı makaleyi inceleyin.

Hedef duruma göre animasyon oluşturma

AnimatedContent composable, hedef duruma göre değişirken içeriğini animasyonla gösterir.

Row {
    var count by remember { mutableIntStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Add")
    }
    AnimatedContent(
        targetState = count,
        label = "animated content"
    ) { targetCount ->
        // Make sure to use `targetCount`, not `count`.
        Text(text = "Count: $targetCount")
    }
}

Varsayılan olarak, ilk içerik silinir ve ardından hedef içerik görünür (bu davranışa geçişli silme adı verilir). transitionSpec parametresine bir ContentTransform nesnesi belirterek bu animasyon davranışını özelleştirebilirsiniz. with infix işlevini kullanarak bir EnterTransition nesnesini bir ExitTransition nesnesiyle birleştirerek ContentTransform örneği oluşturabilirsiniz. SizeTransform öğesini ContentTransform nesnesine using infix işleviyle ekleyerek uygulayabilirsiniz.

AnimatedContent(
    targetState = count,
    transitionSpec = {
        // Compare the incoming number with the previous number.
        if (targetState > initialState) {
            // If the target number is larger, it slides up and fades in
            // while the initial (smaller) number slides up and fades out.
            slideInVertically { height -> height } + fadeIn() togetherWith
                slideOutVertically { height -> -height } + fadeOut()
        } else {
            // If the target number is smaller, it slides down and fades in
            // while the initial number slides down and fades out.
            slideInVertically { height -> -height } + fadeIn() togetherWith
                slideOutVertically { height -> height } + fadeOut()
        }.using(
            // Disable clipping since the faded slide-in/out should
            // be displayed out of bounds.
            SizeTransform(clip = false)
        )
    }, label = "animated content"
) { targetCount ->
    Text(text = "$targetCount")
}

EnterTransition, hedef içeriğin nasıl görüneceğini, ExitTransition ise ilk içeriğin nasıl kaybolacağını tanımlar. AnimatedVisibility için kullanılabilen tüm EnterTransition ve ExitTransition işlevlerine ek olarak AnimatedContent, slideIntoContainer ve slideOutOfContainer özelliklerini sunar. Bunlar, slideInHorizontally/Vertically ve slideOutHorizontally/Vertically için uygun alternatiflerdir. AnimatedContent içeriğinin ilk içeriğinin ve hedef içeriğinin boyutlarına göre kaydırma mesafesini hesaplar.

SizeTransform, boyutun ilk ve hedef içerikler arasında nasıl animasyonlu hale getirileceğini tanımlar. Animasyonu oluştururken hem başlangıç boyutuna hem de hedef boyuta erişebilirsiniz. SizeTransform, animasyonlar sırasında içeriğin bileşen boyutuna kırpılıp kırpılmayacağını da kontrol eder.

var expanded by remember { mutableStateOf(false) }
Surface(
    color = MaterialTheme.colorScheme.primary,
    onClick = { expanded = !expanded }
) {
    AnimatedContent(
        targetState = expanded,
        transitionSpec = {
            fadeIn(animationSpec = tween(150, 150)) togetherWith
                fadeOut(animationSpec = tween(150)) using
                SizeTransform { initialSize, targetSize ->
                    if (targetState) {
                        keyframes {
                            // Expand horizontally first.
                            IntSize(targetSize.width, initialSize.height) at 150
                            durationMillis = 300
                        }
                    } else {
                        keyframes {
                            // Shrink vertically first.
                            IntSize(initialSize.width, targetSize.height) at 150
                            durationMillis = 300
                        }
                    }
                }
        }, label = "size transform"
    ) { targetExpanded ->
        if (targetExpanded) {
            Expanded()
        } else {
            ContentIcon()
        }
    }
}

Alt öğelerin giriş ve çıkış geçişlerine animasyon ekleme

AnimatedVisibility gibi, animateEnterExit değiştiricisi de AnimatedContent öğesinin içerik lambda'sında kullanılabilir. EnterAnimation ve ExitAnimation özelliklerini doğrudan veya dolaylı olarak her bir alt öğeye ayrı ayrı uygulamak için bu yöntemi kullanın.

Özel animasyon ekleme

AnimatedVisibility gibi, transition alanı da AnimatedContent öğesinin içerik lambda'sında kullanılabilir. Bunu, AnimatedContent geçişiyle aynı anda çalışan özel bir animasyon efekti oluşturmak için kullanın. Ayrıntılar için updateTransition bölümüne bakın.

İki düzen arasında animasyon oluşturma

Crossfade, iki düzen arasında çapraz geçiş animasyonuyla geçiş yapar. current parametresine iletilen değer değiştirildiğinde içerik, çapraz geçiş animasyonuyla değiştirilir.

var currentPage by remember { mutableStateOf("A") }
Crossfade(targetState = currentPage, label = "cross fade") { screen ->
    when (screen) {
        "A" -> Text("Page A")
        "B" -> Text("Page B")
    }
}

Yerleşik animasyon değiştiriciler

Compose, belirli değişiklikleri doğrudan composable'lerde canlandırmak için değiştiriciler sağlar.

Composable boyut değişikliklerini canlandırma

Boyut değişikliğini sorunsuz bir şekilde animasyonla gösteren yeşil renkli composable.
Şekil 2. Küçük ve daha büyük bir boyut arasında sorunsuz bir şekilde animasyon oluşturan Composable'lar

animateContentSize değiştiricisi, boyut değişikliğini animasyonlandırır.

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
        }

) {
}

Liste öğesi animasyonları

Lazy list veya Lazy grid içindeki öğelerin yeniden sıralanmasını animasyonlu hale getirmek istiyorsanız Lazy layout item animation documentation (Lazy layout öğe animasyonu dokümanı) başlıklı makaleye göz atın.