Un Brush in Compose descrive come viene disegnato un elemento sullo schermo: determina i colori che vengono disegnati nell'area di disegno (ad es. un cerchio, un quadrato, un percorso). Esistono alcuni pennelli integrati utili per disegnare, come LinearGradient, RadialGradient o un semplice pennello SolidColor.
I pennelli possono essere utilizzati con le chiamate di disegno Modifier.background(), TextStyle o DrawScope per applicare lo stile di pittura ai contenuti che vengono disegnati.
Ad esempio, è possibile applicare un pennello con gradiente orizzontale per disegnare un cerchio in DrawScope:
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
Pennelli con sfumatura
Esistono molti pennelli con sfumature integrati che possono essere utilizzati per ottenere diversi effetti di sfumatura. Questi pennelli ti consentono di specificare l'elenco di colori da cui vuoi creare un gradiente.
Ecco un elenco dei pennelli con gradiente disponibili e del relativo output:
| Tipo di pennello sfumatura | Output |
|---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
Nota: per ottenere una transizione fluida tra i colori, imposta l'ultimo colore sul colore iniziale. |
|
Brush.radialGradient(colorList) |
|
Modifica la distribuzione dei colori con colorStops
Per personalizzare la visualizzazione dei colori nel gradiente, puoi modificare il valore colorStops per ciascuno. colorStops deve essere specificato come frazione,
tra 0 e 1. I valori superiori a 1 faranno sì che questi colori non vengano visualizzati come parte del gradiente.
Puoi configurare i punti di interruzione del colore in modo che abbiano quantità diverse, ad esempio più o meno di un colore:
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)) )
I colori sono dispersi all'offset fornito come definito nella coppia colorStop, con meno giallo rispetto al rosso e al blu.
Ripeti un pattern con TileMode
Ogni pennello sfumatura ha l'opzione per impostare un TileMode. Potresti non notare la TileMode se non hai impostato un inizio e una fine per il gradiente, poiché per impostazione predefinita riempirà l'intera area. Un TileMode affiancherà il gradiente solo se le dimensioni dell'area sono maggiori di quelle del pennello.
Il seguente codice ripeterà il pattern del gradiente 4 volte, poiché endX è impostato su 50.dp e la dimensione è impostata su 200.dp:
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 ) ) )
Di seguito è riportata una tabella che descrive in dettaglio l'effetto delle diverse modalità di riquadro per l'esempio HorizontalGradient riportato sopra:
| TileMode | Output |
|---|---|
TileMode.Repeated: il bordo viene ripetuto dall'ultimo colore al primo. |
|
TileMode.Mirror: il bordo viene specchiato dall'ultimo colore al primo. |
|
TileMode.Clamp: il bordo è limitato al colore finale. Poi, applicherà il colore più vicino al resto della regione. |
|
TileMode.Decal: esegue il rendering solo fino alle dimensioni dei limiti. TileMode.Decal utilizza il nero trasparente per campionare i contenuti al di fuori dei limiti originali, mentre TileMode.Clamp campiona il colore del bordo. |
|
TileMode funziona in modo simile per gli altri gradienti direzionali, con la differenza che la ripetizione avviene nella direzione opposta.
Modifica le dimensioni del pennello
Se conosci le dimensioni dell'area in cui verrà disegnato il pennello, puoi impostare la sezione endX come abbiamo visto sopra nella sezione TileMode. Se ti trovi in un DrawScope, puoi utilizzare la relativa proprietà size per ottenere le dimensioni dell'area.
Se non conosci le dimensioni dell'area di disegno (ad esempio se Brush è assegnato a Text), puoi estendere Shader e utilizzare le dimensioni dell'area di disegno nella funzione createShader.
In questo esempio, dividi la dimensione per 4 per ripetere il pattern 4 volte:
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) )
Puoi anche modificare le dimensioni del pennello di qualsiasi altra sfumatura, ad esempio le sfumature radiali. Se non specifichi una dimensione e un centro, il gradiente occuperà l'intero spazio di DrawScope e il centro della sfumatura radiale corrisponderà per impostazione predefinita al centro dello spazio di DrawScope. In questo modo, il centro della sfumatura radiale viene visualizzato come il centro della dimensione più piccola (larghezza o altezza):
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
Quando la sfumatura radiale viene modificata per impostare la dimensione del raggio sulla dimensione massima, puoi vedere che produce un effetto di sfumatura radiale migliore:
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) )
Vale la pena notare che le dimensioni effettive passate alla creazione dello shader sono determinate dal punto in cui viene richiamato. Per impostazione predefinita, Brush riallocherà internamente il suo Shader se le dimensioni sono diverse dall'ultima creazione di Brush o se un oggetto di stato utilizzato nella creazione dello shader è cambiato.
Il seguente codice crea lo shader tre volte con dimensioni diverse, man mano che le dimensioni dell'area di disegno cambiano:
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) } } } )
Utilizza un'immagine come pennello
Per utilizzare un ImageBitmap come Brush, carica l'immagine come ImageBitmap e crea un pennello ImageShader:
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))
Il pennello viene applicato ad alcuni tipi di disegno diversi: uno sfondo, il testo e la tela. L'output è il seguente:
Tieni presente che ora il testo viene visualizzato anche utilizzando ImageBitmap per colorare i pixel del testo.
Esempio avanzato: pennello personalizzato
Pennello AGSL RuntimeShader
AGSL offre un sottoinsieme di funzionalità degli shader GLSL. Gli shader possono essere scritti in AGSL e utilizzati con un Brush in Compose.
Per creare un pennello Shader, definisci innanzitutto lo Shader come stringa dello Shader AGSL:
@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()
Lo shader riportato sopra accetta due colori di input, calcola la distanza dall'angolo in basso a sinistra (vec2(0, 1)) dell'area di disegno ed esegue un mix tra i due colori in base alla distanza. In questo modo si ottiene un effetto sfumato.
Poi, crea lo Shader Brush e imposta le uniformi per resolution, ovvero le dimensioni dell'area di disegno, e color e color2 che vuoi utilizzare come input per il gradiente personalizzato:
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) ) }
Se esegui questo codice, sullo schermo viene visualizzato quanto segue:
Vale la pena notare che con gli shader puoi fare molto di più dei semplici gradienti, poiché si tratta di calcoli basati sulla matematica. Per saperne di più su AGSL, consulta la documentazione di AGSL.
Risorse aggiuntive
Per altri esempi di utilizzo di Brush in Compose, consulta le seguenti risorse:
- Animating brush Text coloring in Compose 🖌️
- Grafica e layout personalizzati in Compose - Android Dev Summit 2022
- Esempio di JetLagged - Pennello RuntimeShader
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Modificatori di grafica
- Grafica in Compose
- Stile del testo