Bayangan meningkatkan UI Anda secara visual, menunjukkan interaktivitas kepada pengguna, dan memberikan masukan langsung tentang tindakan pengguna. Compose menyediakan beberapa cara untuk menggabungkan bayangan ke dalam aplikasi Anda:
Modifier.shadow(): Membuat bayangan berbasis elevasi di belakang composable yang sesuai dengan panduan Desain Material.Modifier.dropShadow(): Membuat bayangan yang dapat disesuaikan yang muncul di belakang composable, sehingga membuatnya tampak lebih tinggi.Modifier.innerShadow(): Membuat bayangan di dalam batas composable, sehingga tampak seperti ditekan ke permukaan di belakangnya.
Modifier.shadow() cocok untuk membuat bayangan dasar, sedangkan pengubah
dropShadow() dan innerShadow() menawarkan kontrol yang lebih terperinci dan
presisi atas rendering bayangan.
Halaman ini menjelaskan cara menerapkan setiap pengubah ini, termasuk cara
menganimasikan bayangan saat interaksi pengguna dan cara merangkai
pengubah innerShadow() dan dropShadow() untuk
membuat bayangan gradien,
bayangan neumorfik, dan lainnya.
Membuat bayangan dasar
Modifier.shadow() membuat bayangan dasar mengikuti panduan Desain Material yang menyimulasikan sumber cahaya di atas kepala. Kedalaman bayangan didasarkan pada nilai elevation, dan bayangan yang dihasilkan dipangkas sesuai bentuk composable.
@Composable fun ElevationBasedShadow() { Box( modifier = Modifier.aspectRatio(1f).fillMaxSize(), contentAlignment = Alignment.Center ) { Box( Modifier .size(100.dp, 100.dp) .shadow(10.dp, RectangleShape) .background(Color.White) ) } }
Modifier.shadow().Menerapkan drop shadow
Gunakan pengubah dropShadow() untuk menggambar bayangan yang akurat di belakang
konten, yang membuat elemen tampak ditinggikan.
Anda dapat mengontrol aspek utama berikut melalui parameter Shadow:
radius: Menentukan kelembutan dan difusi blur Anda.color: Menentukan warna tint.offset: Memosisikan geometri bayangan di sepanjang sumbu x dan y.spread: Mengontrol perluasan atau penyusutan geometri bayangan.
Selain itu, parameter shape menentukan bentuk keseluruhan bayangan. Fungsi ini dapat
menggunakan geometri apa pun dari paket androidx.compose.foundation.shape, serta
bentuk Material Ekspresif.
Untuk menerapkan Drop Shadow dasar, tambahkan pengubah dropShadow() ke rantai composable, dengan memberikan radius, warna, dan penyebaran. Perhatikan bahwa
latar belakang purpleColor yang muncul di atas bayangan digambar setelah
pengubah dropShadow():
@Composable fun SimpleDropShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(300.dp) .dropShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 6.dp, color = Color(0x40000000), offset = DpOffset(x = 4.dp, 4.dp) ) ) .align(Alignment.Center) .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) ) { Text( "Drop Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }
Poin penting tentang kode
- Pengubah
dropShadow()diterapkan keBoxdalam. Bayangan memiliki karakteristik berikut:- Bentuk persegi panjang bersudut bulat (
RoundedCornerShape(20.dp)) - Radius blur
10.dp, membuat tepi menjadi lembut dan tersebar - Penyebaran
6.dp, yang memperluas ukuran bayangan dan membuatnya lebih besar dari kotak yang memproyeksikannya - Alfa
0.5f, yang membuat bayangan menjadi semi-transparan
- Bentuk persegi panjang bersudut bulat (
- Setelah bayangan ditentukan, .Pengubah
background()diterapkan.Boxdiisi dengan warna putih.- Latar belakang dipotong ke bentuk persegi panjang bersudut lengkung yang sama dengan bayangan.
Hasil
Menerapkan bayangan dalam
Untuk membuat efek terbalik pada dropShadow(), gunakan
Modifier.innerShadow(), yang menciptakan ilusi bahwa elemen
tersembunyi atau ditekan ke permukaan di bawahnya.
Urutan sangat penting saat membuat bayangan dalam. Pengubah innerShadow()
digambar di atas konten. Untuk memastikan bayangan terlihat, Anda biasanya
melakukan langkah-langkah berikut:
- Gambar konten latar belakang Anda.
- Terapkan pengubah
innerShadow()untuk membuat tampilan cekung.
Jika innerShadow() ditempatkan sebelum latar belakang, latar belakang akan digambar
di atas bayangan, sehingga menyembunyikannya sepenuhnya.
Contoh berikut menunjukkan penerapan innerShadow() pada
RoundedCornerShape:
@Composable fun SimpleInnerShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) // note that the background needs to be defined before defining the inner shadow .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) .innerShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 2.dp, color = Color(0x40000000), offset = DpOffset(x = 6.dp, 7.dp) ) ) ) { Text( "Inner Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }
Modifier.innerShadow() pada persegi panjang
sudut membulat.Menganimasikan bayangan pada interaksi pengguna
Agar bayangan merespons interaksi pengguna, Anda dapat mengintegrasikan properti bayangan dengan API animasi Compose. Misalnya, saat pengguna menekan tombol, bayangan dapat berubah untuk memberikan masukan visual instan.
Kode berikut membuat efek "ditekan" dengan bayangan (ilusi bahwa permukaan ditekan ke dalam layar):
@Composable fun AnimatedColoredShadows() { SnippetsTheme { Box(Modifier.fillMaxSize()) { val interactionSource = remember { MutableInteractionSource() } val isPressed by interactionSource.collectIsPressedAsState() // Create transition with pressed state val transition = updateTransition( targetState = isPressed, label = "button_press_transition" ) fun <T> buttonPressAnimation() = tween<T>( durationMillis = 400, easing = EaseInOut ) // Animate all properties using the transition val shadowAlpha by transition.animateFloat( label = "shadow_alpha", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) 0f else 1f } // ... val blueDropShadow by transition.animateColor( label = "shadow_color", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) Color.Transparent else blueDropShadowColor } // ... Box( Modifier .clickable( interactionSource, indication = null ) { // ** ...... **// } .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = blueDropShadow, offset = DpOffset(x = 0.dp, -(2).dp), alpha = shadowAlpha ) ) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = darkBlueDropShadow, offset = DpOffset(x = 2.dp, 6.dp), alpha = shadowAlpha ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color(0xFFFFFFFF), shape = RoundedCornerShape(70.dp) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 8.dp, spread = 4.dp, color = innerShadowColor2, offset = DpOffset(x = 4.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 20.dp, spread = 4.dp, color = innerShadowColor1, offset = DpOffset(x = 4.dp, 0.dp), alpha = innerShadowAlpha ) ) ) { Text( "Animated Shadows", // ... ) } } } }
Poin penting tentang kode
- Mendeklarasikan status awal dan akhir untuk parameter yang akan dianimasikan saat ditekan
dengan
transition.animateColordantransition.animateFloat. - Menggunakan
updateTransitiondan menyediakannya dengantargetState (targetState = isPressed)yang dipilih untuk memverifikasi bahwa semua animasi disinkronkan. Setiap kaliisPressedberubah, objek transisi akan otomatis mengelola animasi semua properti turunan dari nilai saat ini ke nilai target baru. - Menentukan spesifikasi
buttonPressAnimation, yang mengontrol pengaturan waktu dan easing transisi. Kode ini menentukantween(singkatan dari in-between) dengan durasi 400 milidetik dan kurvaEaseInOut, yang berarti animasi dimulai dengan lambat, dipercepat di tengah, dan diperlambat di akhir. - Menentukan
Boxdengan rantai fungsi pengubah yang menerapkan semua properti animasi untuk membuat elemen visual, termasuk hal berikut:- .
clickable(): Pengubah yang membuatBoxmenjadi interaktif. .dropShadow(): Dua drop shadow luar diterapkan terlebih dahulu. Properti warna dan alfanya ditautkan ke nilai animasi (blueDropShadow, dll.) dan membuat tampilan awal yang ditinggikan..innerShadow(): Dua bayangan dalam digambar di atas latar belakang. Propertinya ditautkan ke kumpulan nilai animasi lainnya (innerShadowColor1, dll.) dan membuat tampilan yang menjorok ke dalam.
- .
Hasil
Membuat bayangan gradien
Bayangan tidak terbatas pada warna solid. API bayangan menerima Brush, yang
memungkinkan Anda membuat bayangan gradien.
Box( modifier = Modifier .width(240.dp) .height(200.dp) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = animatedSpread.dp, brush = Brush.sweepGradient( colors ), offset = DpOffset(x = 0.dp, y = 0.dp), alpha = animatedAlpha ) ) .clip(RoundedCornerShape(70.dp)) .background(Color(0xEDFFFFFF)), contentAlignment = Alignment.Center ) { Text( text = breathingText, color = Color.Black, style = MaterialTheme.typography.bodyLarge ) }
Poin penting tentang kode
dropShadow()menambahkan bayangan di belakang kotak.brush = Brush.sweepGradient(colors)mewarnai bayangan dengan gradien yang berputar melalui daftarcolorsyang telah ditentukan, sehingga menciptakan efek seperti pelangi.
Hasil
Anda dapat menggunakan kuas sebagai bayangan untuk membuat gradien dropShadow() dengan
animasi "bernafas":
Menggabungkan bayangan
Anda dapat menggabungkan dan melapisi pengubah dropShadow() dan innerShadow() untuk
membuat berbagai efek. Bagian berikut menunjukkan cara menghasilkan
bayangan neumorphic, neobrutalist, dan realistis dengan teknik ini.
Membuat bayangan neumorfik
Bayangan neumorfik ditandai dengan tampilan lembut yang muncul secara organik dari latar belakang. Untuk membuat bayangan neumorphic, lakukan hal berikut:
- Gunakan elemen yang memiliki warna yang sama dengan latar belakangnya.
- Terapkan dua drop shadow yang samar dan berlawanan: bayangan terang ke satu sudut, dan bayangan gelap ke sudut yang berlawanan.
Cuplikan berikut melapisi dua pengubah dropShadow() untuk membuat
efek neumorfik:
@Composable fun NeumorphicRaisedButton( shape: RoundedCornerShape = RoundedCornerShape(30.dp) ) { val bgColor = Color(0xFFe0e0e0) val lightShadow = Color(0xFFFFFFFF) val darkShadow = Color(0xFFb1b1b1) val upperOffset = -10.dp val lowerOffset = 10.dp val radius = 15.dp val spread = 0.dp Box( modifier = Modifier .fillMaxSize() .background(bgColor) .wrapContentSize(Alignment.Center) .size(240.dp) .dropShadow( shape, shadow = Shadow( radius = radius, color = lightShadow, spread = spread, offset = DpOffset(upperOffset, upperOffset) ), ) .dropShadow( shape, shadow = Shadow( radius = radius, color = darkShadow, spread = spread, offset = DpOffset(lowerOffset, lowerOffset) ), ) .background(bgColor, shape) ) }
Membuat bayangan neobrutalis
Gaya neobrutalis menampilkan tata letak kotak-kotak dengan kontras tinggi, warna-warna cerah,
dan batas tebal. Untuk membuat efek ini, gunakan dropShadow() dengan blur nol
dan offset yang berbeda, seperti yang ditunjukkan dalam cuplikan berikut:
@Composable fun NeoBrutalShadows() { SnippetsTheme { val dropShadowColor = Color(0xFF007AFF) val borderColor = Color(0xFFFF2D55) Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(0.dp), shadow = Shadow( radius = 0.dp, spread = 0.dp, color = dropShadowColor, offset = DpOffset(x = 8.dp, 8.dp) ) ) .border( 8.dp, borderColor ) .background( color = Color.White, shape = RoundedCornerShape(0.dp) ) ) { Text( "Neobrutal Shadows", modifier = Modifier.align(Alignment.Center), style = MaterialTheme.typography.bodyMedium ) } } } }
Membuat bayangan realistis
Bayangan realistis meniru bayangan di dunia fisik—bayangan tersebut tampak diterangi oleh
sumber cahaya utama, sehingga menghasilkan bayangan langsung dan bayangan yang lebih
menyebar. Anda dapat menumpuk beberapa instance dropShadow() dan innerShadow() dengan
properti yang berbeda untuk membuat ulang efek bayangan yang realistis, seperti yang ditunjukkan dalam
cuplikan berikut:
@Composable fun RealisticShadows() { Box(Modifier.fillMaxSize()) { val dropShadowColor1 = Color(0xB3000000) val dropShadowColor2 = Color(0x66000000) val innerShadowColor1 = Color(0xCC000000) val innerShadowColor2 = Color(0xFF050505) val innerShadowColor3 = Color(0x40FFFFFF) val innerShadowColor4 = Color(0x1A050505) Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 40.dp, spread = 0.dp, color = dropShadowColor1, offset = DpOffset(x = 2.dp, 8.dp) ) ) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 0.dp, color = dropShadowColor2, offset = DpOffset(x = 0.dp, 4.dp) ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color.Black, shape = RoundedCornerShape(100.dp) ) // // .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 3.dp, color = innerShadowColor1, offset = DpOffset(x = 6.dp, 6.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 1.dp, color = Color.White, offset = DpOffset(x = 5.dp, 5.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 5.dp, color = innerShadowColor2, offset = DpOffset(x = (-3).dp, (-12).dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 10.dp, color = innerShadowColor3, offset = DpOffset(x = 0.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 9.dp, color = innerShadowColor4, offset = DpOffset(x = 1.dp, 1.dp) ) ) ) { Text( "Realistic Shadows", modifier = Modifier.align(Alignment.Center), fontSize = 24.sp, color = Color.White ) } } }
Poin penting tentang kode
- Dua pengubah
dropShadow()berantai dengan properti yang berbeda diterapkan, diikuti dengan pengubahbackground(). - Pengubah
innerShadow()berantai diterapkan untuk membuat efek tepi metalik di sekitar tepi komponen.
Hasil
Cuplikan kode sebelumnya menghasilkan hal berikut: