Menyesuaikan transisi elemen bersama

Untuk menyesuaikan cara animasi transisi elemen bersama berjalan, ada beberapa parameter yang dapat digunakan untuk mengubah cara transisi elemen bersama.

Spesifikasi animasi

Untuk mengubah spesifikasi animasi yang digunakan untuk pergerakan ukuran dan posisi, Anda dapat menentukan parameter boundsTransform yang berbeda pada Modifier.sharedElement(). Hal ini memberikan posisi Rect awal dan posisi Rect target.

Misalnya, untuk membuat teks dalam contoh sebelumnya bergerak dengan gerakan busur, tentukan parameter boundsTransform untuk menggunakan spesifikasi keyframes:

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
    )
)

Anda dapat menggunakan AnimationSpec apa pun. Contoh ini menggunakan spesifikasi keyframes.

Gambar 1. Contoh yang menunjukkan berbagai parameter boundsTransform

Mode ubah ukuran

Saat menganimasikan antara dua batas bersama, Anda dapat menyetel parameter resizeMode ke RemeasureToBounds atau ScaleToBounds. Parameter ini menentukan cara elemen bersama bertransisi di antara kedua status. ScaleToBounds pertama-tama mengukur tata letak turunan dengan batasan lihat ke depan (atau target). Kemudian, tata letak stabil anak diskalakan agar sesuai dengan batas bersama. ScaleToBounds dapat dianggap sebagai "skala grafis" antara status.

Sebaliknya, RemeasureToBounds mengukur ulang dan menata ulang tata letak turunan sharedBounds dengan batasan tetap yang dianimasikan berdasarkan ukuran target. Pengukuran ulang dipicu oleh perubahan ukuran batas, yang berpotensi terjadi setiap frame.

Untuk composable Text, ScaleToBounds direkomendasikan, karena menghindari tata letak ulang dan penataan ulang teks ke baris yang berbeda. RemeasureToBounds direkomendasikan untuk batas yang memiliki rasio aspek berbeda, dan jika Anda menginginkan kontinuitas yang lancar di antara dua elemen bersama.

Perbedaan antara kedua mode pengubahan ukuran dapat dilihat dalam contoh berikut:

ScaleToBounds

RemeasureToBounds

Lewati ke tata letak akhir

Secara default, saat bertransisi antara dua tata letak, ukuran tata letak akan dianimasikan antara status awal dan akhirnya. Perilaku ini mungkin tidak diinginkan saat menganimasikan konten seperti teks.

Contoh berikut mengilustrasikan teks deskripsi "Lorem Ipsum" yang masuk ke layar dengan dua cara berbeda. Pada contoh pertama, teks akan diatur ulang saat masuk seiring dengan bertambahnya ukuran penampung. Pada contoh kedua, teks tidak mengalir kembali saat bertambah besar. Menambahkan Modifier.skipToLookaheadSize() akan mencegah penataan ulang saat bertambah besar.

Tidak ada Modifier.skipToLookahead() - perhatikan teks "Lorem Ipsum" yang mengalir ulang

Modifier.skipToLookahead() - perhatikan bahwa teks "Lorem Ipsum" mempertahankan status akhirnya di awal animasi

Klip dan overlay

Agar elemen bersama dapat berbagi antara composable yang berbeda, rendering composable diangkat ke overlay lapisan saat transisi dimulai ke kecocokannya di tujuan. Efeknya adalah objek akan keluar dari batas induk dan transformasi layernya (misalnya, alfa dan skala).

Elemen ini akan dirender di atas elemen UI non-bersama lainnya. Setelah transisi selesai, elemen akan dikeluarkan dari overlay ke DrawScope-nya sendiri.

Untuk menggunting elemen bersama ke bentuk, gunakan fungsi Modifier.clip() standar. Tempatkan setelah sharedElement():

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
)

Jika Anda perlu memastikan bahwa elemen bersama tidak pernah dirender di luar penampung induk, Anda dapat menyetel clipInOverlayDuringTransition pada sharedElement(). Secara default, untuk batas bersama bertingkat, clipInOverlayDuringTransition menggunakan jalur klip dari sharedBounds() induk.

Untuk mendukung agar elemen UI tertentu, seperti panel bawah atau tombol tindakan mengambang, selalu berada di atas selama transisi elemen bersama, gunakan Modifier.renderInSharedTransitionScopeOverlay(). Secara default, pengubah ini akan mempertahankan konten di overlay selama waktu saat transisi bersama aktif.

Misalnya, di Jetsnack, BottomAppBar harus ditempatkan di atas elemen bersama hingga layar tidak terlihat. Menambahkan pengubah ke composable akan membuatnya tetap berada di atas.

Tanpa Modifier.renderInSharedTransitionScopeOverlay()

Dengan Modifier.renderInSharedTransitionScopeOverlay()

Anda mungkin ingin composable yang tidak dibagikan dianimasikan saat keluar dan tetap berada di atas composable lain sebelum transisi. Dalam kasus tersebut, gunakan renderInSharedTransitionScopeOverlay().animateEnterExit() untuk menganimasikan keluar composable saat transisi elemen bersama berjalan:

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

Gambar 2. Panel aplikasi bawah meluncur masuk dan keluar saat transisi animasi.

Dalam kasus yang jarang terjadi, jika Anda tidak ingin elemen bersama dirender dalam overlay, Anda dapat menyetel renderInOverlayDuringTransition pada sharedElement() ke false.

Memberi tahu tata letak turunan tentang perubahan ukuran elemen bersama

Secara default, sharedBounds() dan sharedElement() tidak memberi tahu penampung induk tentang perubahan ukuran apa pun saat transisi tata letak.

Untuk menyebarkan perubahan ukuran ke penampung induk saat bertransisi, ubah parameter placeHolderSize menjadi PlaceHolderSize.animatedSize. Melakukannya akan menyebabkan item bertambah besar atau mengecil. Semua item lain dalam tata letak merespons perubahan.

PlaceholderSize.contentSize (default)

PlaceholderSize.animatedSize

(Perhatikan bagaimana item lain dalam daftar bergerak ke bawah sebagai respons terhadap satu item yang membesar)