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

Oluşturma, birçok yerleşik animasyon mekanizmasına sahiptir ve bir araç oluşturmayı, hangisini seçeceğinizi bilmeniz gerekir. Aşağıda, animasyonların yaygın kullanım alanlarının bir listesi yer almaktadır. 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şenin ortak özelliklerini nasıl canlandırabileceğiniz gösterilmektedir.

Görünen / kaybolan animasyon

Kendisini gösteren ve gizleyen yeşil bir bileşim
Şekil 1. Sütundaki bir öğenin görünümünü ve kaybolmasını canlandırma
'nı inceleyin.

Bir Besteci'yi gizlemek veya göstermek için AnimatedVisibility simgesini kullanın. İçerideki çocuklar AnimatedVisibility, kendi girişimi için Modifier.animateEnterExit() kullanabilir tıklayın veya geçişten çıkın.

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. dokümanlarına bakın.

Bir composable'ın görünürlüğünü canlandırmanın bir başka yolu da animateFloatAsState kullanarak zaman içinde alfa sürümü:

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 composable'ın alfasına animasyon ekleme
'nı inceleyin.

Arka plan rengini animasyonlu hale getirme

Renklerin birbirine dönüştüğü bir animasyon olarak zaman içinde değişen arka plan rengiyle oluşturulabilir.
Ş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() kullanımına kıyasla daha etkilidir. 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 büyük boyutlar arasında sorunsuz animasyonlar oluşturma
'nı inceleyin.

Oluşturma, composable'ların boyutunu birkaç farklı şekilde canlandırmanızı sağlar. Birleştirilebilir boyut değişiklikleri arasındaki animasyonlar için animateContentSize() kullanın.

Örneğin, bir satırdan birden fazla satıra 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'i de kullanabilirsiniz.

Bir composable'ın konumunu canlandır

Aşağı ve sağa doğru düzgün şekilde animasyon yapan yeşil composable
Şekil 5. Bir ofsetle hareket eden kompozisyonlar

Bir composable'ın konumunu canlandırmak için Modifier.offset{ } öğesini şununla birlikte kullanın: animateIntOffsetAsState().

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 composable'ın dolgusunu canlandırma

Yeşil kompozisyon, tıklandığında daha küçük ve daha büyük hale gelirken dolgu animasyonlu olur.
Şekil 7. Dolgusu animasyonla oluşturulabilir
'nı inceleyin.

Bir composable'ın dolgusunu canlandırmak için animateDpAsState öğesini şununla birlikte kullanın: Modifier.padding():

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ıklamayla animasyon eklemesi

Bir composable'ın yüksekliğine animasyon eklemek için animateDpAsState öğesini Modifier.graphicsLayer{ }. Tek seferlik yükseklik değişiklikleri için Modifier.shadow() Gölgeyi animasyon haline getiriyorsanız Modifier.graphicsLayer{ } değiştiricisi daha yüksek performanslıdır.

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, hareket ettirme veya döndürme işlemlerine animasyon ekleme

Metin olarak derlenebilir söz
Şekil 9. İki boyut arasında metin animasyonu sorunsuz
'nı inceleyin.

Metnin ölçeğini, çevirisini veya döndürülmesini canlandırırken textMotion özelliğini ayarlayın parametresini TextStyle parametresinden TextMotion.Animated olarak ayarlayın. Bu şekilde daha sorunsuz metin animasyonları arasındaki geçişleri gösterir. 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 rengine animasyon eklemek için BasicText composable'da color lambda öğesini 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ş yapın

"Yeşil ekran"da şöyle diyor:
Ş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 şu adresteki dokümanları okuyun: AnimatedContent veya bu blog yayınını okuyun AnimatedContent

Farklı hedeflere giderken animasyon kullan

İki composable, biri yeşil, diğeri "Açılış", diğeri mavi "Detay" (Detay) yazısı ve composable'ı, composable'ın üzerine kaydırarak animasyon yapıyor.
Şekil 12. Gezinme-kompozisyonu kullanarak composable'lar arasında animasyon oluşturma
'nı inceleyin.

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. Ayrıca, varsayılan animasyonu üst düzeydeki tüm hedefler için kullanılır 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(
            // ...
        )
    }
}

Giriş ve çıkış geçişlerinin farklı türleri vardır. farklı efektleri görmek için dokümanları inceleyin.

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

rememberInfiniteTransition öğesini bir infiniteRepeatable ile kullanın Animasyonunuzu sürekli olarak tekrarlamak için animationSpec tuşuna basın. RepeatModes kelimesini şununla değiştirin: ne yapmanız gerektiğini belirtmelisiniz.

Belirli sayıda tekrarlamak için finiteRepeatable öğesini 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, besteye bir composable 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

Birbirinin arasında yeşil okların olduğu ve birbiri ardına hareket eden dört daire.
Şekil 14. Sıralı animasyonun tek tek nasıl ilerlediğini gösteren şema.

Sıralı veya eşzamanlı gerçekleştirmek için Animatable eş zamanlı API'lerini kullanın animasyonları da ekler. Diğer nedenlerin ardından Animatable numaralı telefondan animateTo aranıyor devam etmeden önce önceki animasyonların bitmesini beklemek için her animasyonun bitmesini bekleyin . 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ı animasyonlar 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ş yordam API'leri (Animatable#animateTo() veya animate) kullanın veya Transition API'sini 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ı tesis 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

Compose'daki 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.

İçerik oluşturmanın farklı aşamalarını göz önünde bulundurun: kompozisyon, düzen ve çizim. Eğer animasyonunuz düzen aşamasını değiştiriyorsa, etkilenen tüm composable'ların tekrar çizebilirsiniz. Animasyonunuz çizim aşamasında gerçekleşiyorsa, animasyon düzeninde çalıştırmaya kıyasla daha yüksek performanslı olacaktır. çünkü bu, genel olarak daha az işin olacağı anlamına gelir.

Uygulamanızın animasyon sırasında mümkün olduğunca az işlem yapmasını sağlamak 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 konu hakkında daha fazla bilgi için okuma işlemlerini erteleme bölümüne performans belgelerini inceleyin.

Animasyon zamanlamasını değiştirme

Oluşturma işleminde varsayılan olarak çoğu animasyon için yay animasyonları kullanılır. Yaylar veya fiziğe dayalı animasyonlar daha doğal görünür. Ayrıca bu öğeler, sabit bir süre yerine nesnenin o anki hızı dikkate alınır. Varsayılanı geçersiz kılmak isterseniz yukarıda gösterilen tüm animasyon API'leri animasyonun çalışma şeklini özelleştirmek için animationSpec ayarlayabilir, belirli bir süre boyunca devam etmesini mi yoksa daha daha hareketli mi olmasını istediğinizi seçin.

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

  • spring: Tüm animasyonlar için varsayılan olan fizik tabanlı animasyon. Siz farklı bir animasyon elde etmek için sertliği veya dampingRatio'yu değiştirebilirsiniz. görünüm ve izlenimi sağlar.
  • 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 bitiş değerine anında tutturur.
Alternatif metninizi buraya yazın
Şekil 16. Özellik kümesi yok ve özel bahar özelliği 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: