Paylaşılan öğe geçişini özelleştir

Paylaşılan öğe geçiş animasyonunun nasıl çalışacağını özelleştirmek için paylaşılan öğelerin geçiş şeklini değiştirmek amacıyla kullanılabilecek birkaç parametre vardır.

Animasyon özellikleri

Boyut ve konum hareketi için kullanılan animasyon spesifikasyonunu değiştirmek isterseniz Modifier.sharedElement() üzerinde farklı bir boundsTransform parametresi belirtebilirsiniz. Bu, ilk Rect konumunu ve hedef Rect konumunu sağlar.

Örneğin, önceki örnekteki metnin yay hareketiyle hareket etmesini sağlamak için keyframes spesifikasyonunu kullanacak boundsTransform parametresini belirtin:

val textBoundsTransform = BoundsTransform { initialBounds, targetBounds ->
    keyframes {
        durationMillis = boundsAnimationDurationMillis
        initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing
        targetBounds at boundsAnimationDurationMillis
    }
}
Text(
    "Cupcake", fontSize = 28.sp,
    modifier = Modifier.sharedBounds(
        rememberSharedContentState(key = "title"),
        animatedVisibilityScope = animatedVisibilityScope,
        boundsTransform = textBoundsTransform
    )
)

İstediğiniz AnimationSpec kullanabilirsiniz. Bu örnekte keyframes spesifikasyonu kullanılmıştır.

Şekil 1. Farklı boundsTransform parametrelerini
gösteren örnek

Yeniden boyutlandırma modu

İki paylaşılan sınır arasında animasyon oluştururken resizeMode parametresini RemeasureToBounds veya ScaleToBounds olarak ayarlayabilirsiniz. Bu parametre, paylaşılan öğenin iki durum arasında nasıl geçiş yapacağını belirler. ScaleToBounds, önce alt düzeni ileriye yönelik (veya hedef) kısıtlamalarla ölçer. Ardından, alt öğenin sabit düzeni, paylaşılan sınırlara sığacak şekilde ölçeklendirilir. ScaleToBounds, eyaletler arasında bir "grafik ölçeği" olarak düşünülebilir.

Buna karşılık RemeasureToBounds, hedef boyuta göre animasyonlu sabit kısıtlamalarla sharedBounds öğesinin alt düzenini yeniden ölçer ve yeniden düzenler. Yeniden ölçüm, sınır boyutu değişikliğiyle tetiklenir. Bu değişiklik her karede gerçekleşebilir.

Text bileşenleri için ScaleToBounds kullanılması önerilir. Bu sayede, metnin farklı satırlara yeniden yerleştirilmesi ve yeniden akması önlenir. RemeasureToBounds, farklı en-boy oranlarına sahip sınırlar için ve iki paylaşılan öğe arasında akıcı bir süreklilik istiyorsanız önerilir.

İki yeniden boyutlandırma modu arasındaki farkı aşağıdaki örneklerde görebilirsiniz:

ScaleToBounds

RemeasureToBounds

Paylaşılan öğeleri dinamik olarak etkinleştirme ve devre dışı bırakma

Varsayılan olarak sharedElement() ve sharedBounds(), hedef durumda eşleşen bir anahtar bulunduğunda düzen değişikliklerini animasyonlu hale getirecek şekilde yapılandırılır. Ancak gezinme yönü veya mevcut kullanıcı arayüzü durumu gibi belirli koşullara göre bu animasyonu dinamik olarak devre dışı bırakmak isteyebilirsiniz.

Paylaşılan öğe geçişinin gerçekleşip gerçekleşmeyeceğini kontrol etmek için rememberSharedContentState() öğesine iletilen SharedContentConfig değerini özelleştirebilirsiniz. isEnabled özelliği, paylaşılan öğenin etkin olup olmadığını belirler.

Aşağıdaki örnekte, paylaşılan geçişi yalnızca belirli ekranlar arasında gezinirken (ör. yalnızca A'dan B'ye) etkinleştiren, diğer ekranlar için devre dışı bırakan bir yapılandırmanın nasıl tanımlanacağı gösterilmektedir.

SharedTransitionLayout {
    val transition = updateTransition(currentState)
    transition.AnimatedContent { targetState ->
        // Create the configuration that depends on state changing.
        fun animationConfig() : SharedTransitionScope.SharedContentConfig {
            return object : SharedTransitionScope.SharedContentConfig {
                override val SharedTransitionScope.SharedContentState.isEnabled: Boolean
                    // For this example, we only enable the transition in one direction
                    // from A -> B and not the other way around.
                    get() =
                        transition.currentState == "A" && transition.targetState == "B"
            }
        }
        when (targetState) {
            "A" -> Box(
                modifier = Modifier
                    .sharedElement(
                        rememberSharedContentState(
                            key = "shared_box",
                            config = animationConfig()
                        ),
                        animatedVisibilityScope = this
                    )
                    // ...
            ) {
                // Your content
            }
            "B" -> {
                Box(
                    modifier = Modifier
                        .sharedElement(
                            rememberSharedContentState(
                                key = "shared_box",
                                config = animationConfig()
                            ),
                            animatedVisibilityScope = this
                        )
                        // ...
                ) {
                    // Your content
                }
            }
        }
    }
}

Varsayılan olarak, devam eden bir animasyon sırasında paylaşılan bir öğe devre dışı bırakılırsa devam eden animasyonların yanlışlıkla kaldırılmasını önlemek için mevcut animasyon tamamlanır. Animasyon devam ederken öğeyi kaldırmanız gerekiyorsa SharedContentConfig arayüzünde shouldKeepEnabledForOngoingAnimation yöntemini geçersiz kılarak false döndürmesini sağlayabilirsiniz.

Son düzene atla

Varsayılan olarak, iki düzen arasında geçiş yaparken düzen boyutu, başlangıç ve son durumu arasında animasyonlu olarak değişir. Metin gibi içeriklere animasyon eklerken bu durum istenmeyen bir davranış olabilir.

Aşağıdaki örnekte, "Lorem Ipsum" açıklama metninin ekrana iki farklı şekilde girdiği gösterilmektedir. İlk örnekte, kapsayıcı büyüdükçe metin girerken yeniden düzenlenir. İkinci örnekte ise metin büyüdükçe yeniden akmıyor. Modifier.skipToLookaheadSize() eklemek, büyüdükçe yeniden akışı engeller.

Hayır Modifier.skipToLookaheadSize() - "Lorem Ipsum" metninin yeniden akışına dikkat edin

Modifier.skipToLookaheadSize() - "Lorem Ipsum" metninin animasyonun başında son halini koruduğuna dikkat edin.

Klip ve yer paylaşımları

Paylaşılan öğelerin farklı composable'lar arasında paylaşılması için, hedefteki eşleşmesine geçiş başlatıldığında composable'ın oluşturulması bir katman yer paylaşımına yükseltilir. Bu sayede, üst öğenin sınırlarından ve katman dönüşümlerinden (ör. alfa ve ölçek) kaçar.

Paylaşılmayan diğer kullanıcı arayüzü öğelerinin üzerinde oluşturulur. Geçiş tamamlandığında öğe, yer paylaşımlı reklamdan kendi DrawScope öğesine bırakılır.

Paylaşılan bir öğeyi bir şekle kırpmak için standart Modifier.clip() işlevini kullanın. sharedElement() ifadesinden sonra yerleştirin:

Image(
    painter = painterResource(id = R.drawable.cupcake),
    contentDescription = "Cupcake",
    modifier = Modifier
        .size(100.dp)
        .sharedElement(
            rememberSharedContentState(key = "image"),
            animatedVisibilityScope = this@AnimatedContent
        )
        .clip(RoundedCornerShape(16.dp)),
    contentScale = ContentScale.Crop
)

Paylaşılan bir öğenin hiçbir zaman üst kapsayıcının dışında oluşturulmamasını sağlamanız gerekiyorsa sharedElement() üzerinde clipInOverlayDuringTransition değerini ayarlayabilirsiniz. Varsayılan olarak, iç içe yerleştirilmiş paylaşılan sınırlar için clipInOverlayDuringTransition, üst öğe sharedBounds()'ten gelen kırpma yolunu kullanır.

Alt çubuk veya kayan işlem düğmesi gibi belirli kullanıcı arayüzü öğelerini paylaşılan öğe geçişi sırasında her zaman en üstte tutmak için Modifier.renderInSharedTransitionScopeOverlay() kullanın. Varsayılan olarak bu değiştirici, paylaşılan geçiş etkin olduğu sürece içeriği yer paylaşımında tutar.

Örneğin, Jetsnack'te BottomAppBar, ekran görünmez olana kadar paylaşılan öğenin üzerine yerleştirilmelidir. Değiştiriciyi composable'a eklediğinizde composable yükseltilmiş olarak kalır.

Modifier.renderInSharedTransitionScopeOverlay() olmayanlar

Modifier.renderInSharedTransitionScopeOverlay() ile

Paylaşılmayan composable'ınızın animasyonlu bir şekilde kaybolmasını ve geçişten önce diğer composable'ların üzerinde kalmasını isteyebilirsiniz. Bu gibi durumlarda, paylaşılan öğe geçişi çalışırken composable'ın animasyonunu kaldırmak için renderInSharedTransitionScopeOverlay().animateEnterExit() kullanın:

JetsnackBottomBar(
    modifier = Modifier
        .renderInSharedTransitionScopeOverlay(
            zIndexInOverlay = 1f,
        )
        .animateEnterExit(
            enter = fadeIn() + slideInVertically {
                it
            },
            exit = fadeOut() + slideOutVertically {
                it
            }
        )
)

Şekil 2. Animasyon geçişleri sırasında alt uygulama çubuğu içeri ve dışarı kayar.

Paylaşılan öğenizin bir yer paylaşımında oluşturulmasını istemediğiniz nadir durumlarda, sharedElement() üzerindeki renderInOverlayDuringTransition değerini false olarak ayarlayabilirsiniz.

Paylaşılan öğe boyutundaki değişiklikleri eş düzey düzenlere bildirme

Varsayılan olarak sharedBounds() ve sharedElement(), düzen geçişleri sırasında boyut değişikliklerini üst kapsayıcıya bildirmez.

Boyut değişikliklerini geçiş sırasında üst kapsayıcıya yaymak için placeholderSize parametresini PlaceholderSize.AnimatedSize olarak değiştirin. Bunu yaptığınızda öğe büyür veya küçülür. Düzendeki diğer tüm öğeler bu değişikliğe yanıt verir.

PlaceholderSize.ContentSize (varsayılan)

PlaceholderSize.AnimatedSize

(Listedeki diğer öğelerin, büyüyen öğeye göre nasıl aşağı kaydığına dikkat edin.)