Schatten heben die Benutzeroberfläche visuell hervor, zeigen Nutzern Interaktivität an und geben sofortiges Feedback zu Nutzeraktionen. Compose bietet mehrere Möglichkeiten, Schatten in Ihre App einzubinden:
Modifier.shadow(): Erstellt einen höhenbasierten Schatten hinter einem zusammensetzbaren Element, das den Material Design-Richtlinien entspricht.Modifier.dropShadow(): Erstellt einen anpassbaren Schatten, der hinter einem zusammensetzbaren Element angezeigt wird, sodass es erhöht aussieht.Modifier.innerShadow(): Erstellt einen Schatten innerhalb der Rahmen eines zusammensetzbaren Elements, sodass es so aussieht, als wäre es in die Oberfläche dahinter gedrückt.
Modifier.shadow() eignet sich zum Erstellen einfacher Schatten, während die Modifikatoren dropShadow() und innerShadow() eine genauere Steuerung und Präzision bei der Schattenwiedergabe bieten.
Auf dieser Seite wird beschrieben, wie Sie jeden dieser Modifikatoren implementieren, einschließlich der
Animation von Schatten bei Nutzerinteraktion und der Verkettung der
innerShadow() und dropShadow() Modifikatoren, um
Farbverlaufsschatten,
neumorphe Schatten und mehr zu erstellen.
Einfache Schatten erstellen
Modifier.shadow() erstellt einen einfachen Schatten gemäß den Material Design
Richtlinien, der eine Lichtquelle von oben simuliert. Die Schattentiefe basiert auf einem elevation-Wert und der Schlagschatten wird auf die Form des zusammensetzbaren Elements zugeschnitten.
@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() erstellt wurde.Schlagschatten implementieren
Verwenden Sie den dropShadow() Modifikator, um einen genauen Schatten hinter Ihren
Inhalten zu zeichnen, wodurch das Element erhöht aussieht.
Sie können die folgenden wichtigen Aspekte über den Parameter Shadow steuern:
radius: Definiert die Weichheit und Streuung der Unschärfe.color: Definiert die Farbe des Farbtons.offset: Positioniert die Schattengeometrie entlang der X- und Y-Achse.spread: Steuert die Ausdehnung oder Verkleinerung der Schattengeometrie.
Außerdem definiert der Parameter shape die Gesamtform des Schattens. Er kann
jede Geometrie aus dem Paket androidx.compose.foundation.shape sowie
die Material Expressive-Formen verwenden.
Wenn Sie einen einfachen Schlagschatten implementieren möchten, fügen Sie den Modifikator dropShadow() Ihrer zusammensetzbaren Kette hinzu und geben Sie Radius, Farbe und Streuung an. Der purpleColor-Hintergrund, der über dem Schatten angezeigt wird, wird nach dem Modifikator dropShadow() gezeichnet:
@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 ) } } }
Wichtige Informationen zum Code
- Der Modifikator
dropShadow()wird auf das innereBox-Element angewendet. Der Schatten hat die folgenden Eigenschaften:- Eine abgerundete Rechteckform (
RoundedCornerShape(20.dp)) - Ein Unschärferadius von
10.dp, wodurch die Kanten weich und diffus werden - Eine Streuung von
6.dp, die die Größe des Schattens erweitert und ihn größer als das ihn werfende Feld macht - Ein Alpha von
0.5f, wodurch der Schatten halbtransparent wird
- Eine abgerundete Rechteckform (
- Nachdem der Schatten definiert wurde, wird der Modifikator
background()angewendet.- Das
Box-Element ist weiß. - Der Hintergrund wird auf dieselbe abgerundete Rechteckform wie der Schatten zugeschnitten.
- Das
Ergebnis
Innere Schatten implementieren
Wenn Sie einen umgekehrten Effekt zu dropShadow() erstellen möchten, verwenden Sie
Modifier.innerShadow(). Dadurch entsteht die Illusion, dass ein Element
vertieft oder in die darunter liegende Oberfläche gedrückt ist.
Die Reihenfolge ist beim Erstellen innerer Schatten wichtig. Der Modifikator innerShadow() wird über dem Inhalt gezeichnet. Damit der Schatten sichtbar ist, führen Sie in der Regel die folgenden Schritte aus:
- Zeichnen Sie den Hintergrundinhalt.
- Wenden Sie den Modifikator
innerShadow()an, um das konkave Aussehen zu erstellen.
Wenn innerShadow() vor dem Hintergrund platziert wird, wird der Hintergrund über den Schatten gezeichnet und verdeckt ihn vollständig.
Im folgenden Beispiel wird innerShadow() auf eine RoundedCornerShape angewendet:
@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() auf ein abgerundetes Rechteck.Schatten bei Nutzerinteraktion animieren
Wenn Sie möchten, dass Ihre Schatten auf Nutzerinteraktionen reagieren, können Sie Schatten eigenschaften in die Animations-APIs von Compose einbinden. Wenn ein Nutzer beispielsweise auf eine Schaltfläche klickt, kann sich der Schatten ändern, um sofortiges visuelles Feedback zu geben.
Mit dem folgenden Code wird ein „gedrückter“ Effekt mit einem Schatten erstellt (die Illusion, dass die Oberfläche in den Bildschirm gedrückt wird):
@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", // ... ) } } } }
Wichtige Informationen zum Code
- Deklariert die Start- und Endzustände für die Parameter, die beim Drücken mit
transition.animateColorundtransition.animateFloatanimiert werden sollen. - Verwendet
updateTransitionund stellt den ausgewähltentargetState (targetState = isPressed)bereit, um zu prüfen, ob alle Animationen synchronisiert sind. Wenn sichisPressedändert, verwaltet das Übergangsobjekt automatisch die Animation aller untergeordneten Eigenschaften von ihren aktuellen Werten zu den neuen Zielwerten. - Definiert die Spezifikation
buttonPressAnimation, die das Timing und das Easing des Übergangs steuert. Es wird eintween(kurz für „in-between“) mit einer Dauer von 400 Millisekunden und einerEaseInOut-Kurve angegeben. Das bedeutet, dass die Animation langsam beginnt, in der Mitte schneller wird und am Ende wieder langsamer wird. - Definiert ein
Box-Element mit einer Kette von Modifikatorfunktionen, die alle animierten Eigenschaften anwenden, um das visuelle Element zu erstellen, einschließlich:- .
clickable(): Ein Modifikator, der dasBoxinteraktiv macht. .dropShadow(): Zuerst werden zwei äußere Schlagschatten angewendet. Ihre Farb- und Alpha-Eigenschaften sind mit den animierten Werten (blueDropShadowusw.) verknüpft und erzeugen das anfängliche erhöhte Aussehen..innerShadow(): Zwei innere Schatten werden über dem Hintergrund gezeichnet. Ihre Eigenschaften sind mit den anderen animierten Werten (innerShadowColor1usw.) verknüpft und erzeugen das vertiefte Aussehen.
- .
Ergebnis
Farbverlaufsschatten erstellen
Schatten sind nicht auf Volltonfarben beschränkt. Die Schatten-API akzeptiert einen Brush, mit dem Sie Farbverlaufsschatten erstellen können.
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 ) }
Wichtige Informationen zum Code
dropShadow()fügt hinter dem Feld einen Schatten hinzu.brush = Brush.sweepGradient(colors)färbt den Schatten mit einem Farbverlauf, der eine Liste vordefiniertercolorsdurchläuft und so einen regenbogenartigen Effekt erzeugt.
Ergebnis
Sie können einen Pinsel als Schatten verwenden, um einen dropShadow()-Farbverlauf mit einer „atmenden“ Animation zu erstellen:
Schatten kombinieren
Sie können die Modifikatoren dropShadow() und innerShadow() kombinieren und überlagern, um eine Vielzahl von Effekten zu erzielen. In den folgenden Abschnitten wird gezeigt, wie Sie mit dieser Technik neumorphe, neobrutalistische und realistische Schatten erstellen.
Neumorphe Schatten erstellen
Neumorphe Schatten zeichnen sich durch ein weiches Aussehen aus, das organisch aus dem Hintergrund entsteht. So erstellen Sie neumorphe Schatten:
- Verwenden Sie ein Element, das dieselben Farben wie der Hintergrund hat.
- Wenden Sie zwei schwache, entgegengesetzte Schlagschatten an: einen hellen Schatten in einer Ecke und einen dunklen Schatten in der gegenüberliegenden Ecke.
Im folgenden Snippet werden zwei dropShadow()-Modifikatoren überlagert, um den neumorphen Effekt zu erzielen:
@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) ) }
Neobrutalistische Schatten erstellen
Der neobrutalistische Stil zeichnet sich durch kontrastreiche, blockartige Layouts, lebendige Farben und dicke Rahmen aus. Um diesen Effekt zu erzielen, verwenden Sie einen dropShadow()-Modifikator ohne Unschärfe und mit einem deutlichen Offset, wie im folgenden Snippet gezeigt:
@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 ) } } } }
Realistische Schatten erstellen
Realistische Schatten ahmen Schatten in der physischen Welt nach. Sie sehen so aus, als würden sie von einer primären Lichtquelle beleuchtet, was zu einem direkten und einem diffuseren Schatten führt. Sie können mehrere dropShadow()- und innerShadow()-Instanzen mit unterschiedlichen Eigenschaften stapeln, um realistische Schatteneffekte zu erzeugen, wie im folgenden Snippet gezeigt:
@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 ) } } }
Wichtige Informationen zum Code
- Es werden zwei verkettete
dropShadow()-Modifikatoren mit unterschiedlichen Eigenschaften angewendet, gefolgt von einembackground()-Modifikator. - Verkettete
innerShadow()-Modifikatoren werden angewendet, um den metallischen Randeffekt um die Kante der Komponente zu erzeugen.
Ergebnis
Das vorherige Code-Snippet erzeugt Folgendes: