Ein Brush
in der Funktion „Compose“ beschreibt, wie etwas auf dem Bildschirm dargestellt wird. Es bestimmt die Farbe(n), die im Zeichnungsbereich gezeichnet wird (z. B. ein Kreis, ein Quadrat oder ein Pfad). Es gibt einige integrierte Pinsel, die sich zum Zeichnen eignen, z. B. LinearGradient
, RadialGradient
oder ein einfacher Pinsel für SolidColor
.
Pinsel kann mit den Zeichenaufrufen Modifier.background()
, TextStyle
oder DrawScope
verwendet werden, um den Zeichenstil auf die gezeichneten Inhalte anzuwenden.
Beispielsweise kann ein horizontaler Farbverlaufspinsel auf das Zeichnen eines Kreises in DrawScope
angewendet werden:
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
Farbverlaufpinsel
Es gibt viele integrierte Farbverlaufspinsel, mit denen sich verschiedene Farbverlaufseffekte erzielen lassen. Mit diesen Pinseln kannst du eine Liste der Farben angeben, aus denen du einen Farbverlauf erstellen möchtest.
Eine Liste der verfügbaren Farbverlaufspinsel und ihrer entsprechenden Ausgabe:
Farbverlaufpinseltyp | Ausgang |
---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
Hinweis: Für einen weichen Übergang zwischen den Farben sollten Sie als Startfarbe die letzte Farbe festlegen. |
|
Brush.radialGradient(colorList) |
Farbverteilung mit colorStops
ändern
Um die Darstellung der Farben im Farbverlauf anzupassen, können Sie den colorStops
-Wert für jede Farbe anpassen. colorStops
muss als Bruch zwischen 0 und 1 angegeben werden. Werte über 1 führen dazu, dass diese Farben nicht als Teil des Farbverlaufs gerendert werden.
Sie können die Farbstopps so konfigurieren, dass sie unterschiedliche Mengen haben, z. B. weniger oder mehr für eine Farbe:
val colorStops = arrayOf( 0.0f to Color.Yellow, 0.2f to Color.Red, 1f to Color.Blue ) Box( modifier = Modifier .requiredSize(200.dp) .background(Brush.horizontalGradient(colorStops = colorStops)) )
Die Farben sind mit dem für das Paar colorStop
definierten Offset verteilt, also weniger Gelb als Rot und Blau.
Muster mit TileMode
wiederholen
Für jeden Farbverlaufspinsel kann ein TileMode
festgelegt werden. Sie bemerken TileMode
möglicherweise nicht, wenn Sie keinen Start und kein Ende für den Farbverlauf festgelegt haben, da er standardmäßig den gesamten Bereich ausfüllt. TileMode
kachelt den Farbverlauf nur dann, wenn die Größe des Bereichs größer als die Pinselgröße ist.
Mit dem folgenden Code wird das Farbverlaufsmuster 4-mal wiederholt, da endX
auf 50.dp
und die Größe auf 200.dp
gesetzt ist:
val listColors = listOf(Color.Yellow, Color.Red, Color.Blue) val tileSize = with(LocalDensity.current) { 50.dp.toPx() } Box( modifier = Modifier .requiredSize(200.dp) .background( Brush.horizontalGradient( listColors, endX = tileSize, tileMode = TileMode.Repeated ) ) )
In der folgenden Tabelle wird die Wirkung der verschiedenen Kachelmodi für das obige HorizontalGradient
-Beispiel beschrieben:
Kachelmodus | Ausgang |
---|---|
TileMode.Repeated : Der Rand wird von der letzten Farbe zur ersten Farbe wiederholt. |
|
TileMode.Mirror : Der Rand wird von der letzten zur ersten Farbe gespiegelt. |
|
TileMode.Clamp : Rand wird an die endgültige Farbe gebunden. Dann wird die nächstgelegene Farbe für den Rest der Region verwendet. |
|
TileMode.Decal : Nur bis zur Größe der Grenzen wird gerendert. Bei TileMode.Decal wird transparentes Schwarz verwendet, um Inhalte außerhalb der ursprünglichen Begrenzungen zu erfassen. Bei TileMode.Clamp hingegen wird die Kantenfarbe entnommen. |
TileMode
funktioniert auf ähnliche Weise für die anderen richtungsweisenden Gradienten, wobei der Unterschied in der Richtung der Wiederholung liegt.
Zeichenwerkzeug ändern
Wenn Sie die Größe des Bereichs kennen, in dem der Pinsel gezeichnet wird, können Sie die endX
der Kachel festlegen, wie oben im Abschnitt TileMode
gezeigt. Wenn du dich in einer DrawScope
befindest, kannst du deren size
-Eigenschaft verwenden, um die Größe des Bereichs abzurufen.
Wenn Sie die Größe des Zeichenbereichs nicht kennen (z. B. wenn Brush
„Text“ zugewiesen ist), können Sie Shader
erweitern und die Größe des Zeichenbereichs in der Funktion createShader
verwenden.
Teilen Sie in diesem Beispiel die Größe durch 4, um das Muster viermal zu wiederholen:
val listColors = listOf(Color.Yellow, Color.Red, Color.Blue) val customBrush = remember { object : ShaderBrush() { override fun createShader(size: Size): Shader { return LinearGradientShader( colors = listColors, from = Offset.Zero, to = Offset(size.width / 4f, 0f), tileMode = TileMode.Mirror ) } } } Box( modifier = Modifier .requiredSize(200.dp) .background(customBrush) )
Sie können auch die Pinselgröße eines anderen Farbverlaufs ändern, z. B. radiale Farbverläufe. Wenn Sie keine Größe und keinen Mittelpunkt angeben, belegt der Farbverlauf die vollständigen Grenzen von DrawScope
und der Mittelpunkt des radialen Farbverlaufs liegt standardmäßig im Mittelpunkt der DrawScope
-Grenzen. Dies führt dazu, dass der Mittelpunkt des radialen Farbverlaufs als Mittelpunkt der kleineren Dimension (entweder Breite oder Höhe) erscheint:
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
Wenn Sie den radialen Farbverlauf ändern, um die Radiusgröße auf die maximale Dimension zu setzen, erzielen Sie einen besseren radialen Farbverlauf:
val largeRadialGradient = object : ShaderBrush() { override fun createShader(size: Size): Shader { val biggerDimension = maxOf(size.height, size.width) return RadialGradientShader( colors = listOf(Color(0xFF2be4dc), Color(0xFF243484)), center = size.center, radius = biggerDimension / 2f, colorStops = listOf(0f, 0.95f) ) } } Box( modifier = Modifier .fillMaxSize() .background(largeRadialGradient) )
Beachten Sie, dass die tatsächliche Größe, die bei der Erstellung des Shaders übergeben wird, dort bestimmt wird, wo sie aufgerufen wird. Standardmäßig weist Brush
seine Shader
intern neu zu, wenn sich die Größe von der letzten Erstellung des Brush
unterscheidet oder wenn sich ein Statusobjekt, das zum Erstellen des Shaders verwendet wurde, geändert hat.
Mit dem folgenden Code wird der Shader drei verschiedene Male mit unterschiedlichen Größen erstellt, wenn sich die Größe des Zeichenbereichs ändert:
val colorStops = arrayOf( 0.0f to Color.Yellow, 0.2f to Color.Red, 1f to Color.Blue ) val brush = Brush.horizontalGradient(colorStops = colorStops) Box( modifier = Modifier .requiredSize(200.dp) .drawBehind { drawRect(brush = brush) // will allocate a shader to occupy the 200 x 200 dp drawing area inset(10f) { /* Will allocate a shader to occupy the 180 x 180 dp drawing area as the inset scope reduces the drawing area by 10 pixels on the left, top, right, bottom sides */ drawRect(brush = brush) inset(5f) { /* will allocate a shader to occupy the 170 x 170 dp drawing area as the inset scope reduces the drawing area by 5 pixels on the left, top, right, bottom sides */ drawRect(brush = brush) } } } )
Bild als Pinsel verwenden
Wenn du eine ImageBitmap als Brush
verwenden möchtest, lade das Bild als ImageBitmap
hoch und erstelle einen ImageShader
-Pinsel:
val imageBrush = ShaderBrush(ImageShader(ImageBitmap.imageResource(id = R.drawable.dog))) // Use ImageShader Brush with background Box( modifier = Modifier .requiredSize(200.dp) .background(imageBrush) ) // Use ImageShader Brush with TextStyle Text( text = "Hello Android!", style = TextStyle( brush = imageBrush, fontWeight = FontWeight.ExtraBold, fontSize = 36.sp ) ) // Use ImageShader Brush with DrawScope#drawCircle() Canvas(onDraw = { drawCircle(imageBrush) }, modifier = Modifier.size(200.dp))
Der Pinsel wird auf verschiedene Arten von Zeichnungen angewendet: Hintergrund, Text und Canvas. Dadurch wird Folgendes ausgegeben:
Beachten Sie, dass der Text jetzt ebenfalls mit ImageBitmap
gerendert wird, um die Pixel für den Text darzustellen.
Erweitertes Beispiel: Benutzerdefinierter Pinsel
AGSL: RuntimeShader
-Pinsel
AGSL bietet einen Teil der GLSL-Shader-Funktionen. Shader können in AGSL geschrieben und mit einem Pinsel in Compose verwendet werden.
Definieren Sie zum Erstellen eines Shader-Pinsels zuerst den Shader als AGSL-Shader-String:
@Language("AGSL") val CUSTOM_SHADER = """ uniform float2 resolution; layout(color) uniform half4 color; layout(color) uniform half4 color2; half4 main(in float2 fragCoord) { float2 uv = fragCoord/resolution.xy; float mixValue = distance(uv, vec2(0, 1)); return mix(color, color2, mixValue); } """.trimIndent()
Der obige Shader nimmt zwei Eingabefarben, berechnet den Abstand von unten links (vec2(0, 1)
) des Zeichnungsbereichs und führt basierend auf der Entfernung eine mix
zwischen den beiden Farben durch. Dadurch entsteht ein Farbverlaufseffekt.
Erstellen Sie dann den Shader-Pinsel und legen Sie die Uniformen für resolution
fest. Das ist die Größe des Zeichenbereichs sowie die color
und color2
, die Sie als Eingabe für den benutzerdefinierten Farbverlauf verwenden möchten:
val Coral = Color(0xFFF3A397) val LightYellow = Color(0xFFF8EE94) @RequiresApi(Build.VERSION_CODES.TIRAMISU) @Composable @Preview fun ShaderBrushExample() { Box( modifier = Modifier .drawWithCache { val shader = RuntimeShader(CUSTOM_SHADER) val shaderBrush = ShaderBrush(shader) shader.setFloatUniform("resolution", size.width, size.height) onDrawBehind { shader.setColorUniform( "color", android.graphics.Color.valueOf( LightYellow.red, LightYellow.green, LightYellow .blue, LightYellow.alpha ) ) shader.setColorUniform( "color2", android.graphics.Color.valueOf( Coral.red, Coral.green, Coral.blue, Coral.alpha ) ) drawRect(shaderBrush) } } .fillMaxWidth() .height(200.dp) ) }
Dadurch wird Folgendes auf dem Bildschirm gerendert:
Es ist zu beachten, dass Sie mit Shadern viel mehr als nur mit Farbverläufen tun können, da es sich um alles Mathematik-basierte Berechnungen handelt. Weitere Informationen zu AGSL finden Sie in der AGSL-Dokumentation.
Weitere Informationen
Weitere Beispiele für die Verwendung des Pinsels in der Funktion „Compose“ finden Sie in den folgenden Ressourcen:
- Animierte Pinseltextfarbe in Compose 🖌️
- Benutzerdefinierte Grafiken und Layouts in Compose – Android Dev Summit 2022
- JetLagged Sample – RuntimeShader Brush
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Grafikmodifikatoren
- Grafiken in Compose
- Text formatieren