Un Brush
en Compose describe cómo se dibuja un elemento en la pantalla: determina los colores que se trazan en el área de dibujo (es decir, un círculo, un cuadrado, una ruta). Hay algunos modificadores Brush integrados que son útiles para dibujar, como LinearGradient
, RadialGradient
o un Brush SolidColor
sin formato.
Los modificadores Brush se pueden usar con Modifier.background()
, TextStyle
o llamadas de dibujo DrawScope
para aplicar un estilo de pintura al contenido que se dibuja.
Por ejemplo, se puede aplicar un Brush de gradiente horizontal para dibujar un círculo en DrawScope
:
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
Pinceles degradados
Hay muchos modificadores Brush de gradiente integrados que se pueden usar para lograr diferentes efectos de gradiente. Estos permiten especificar la lista de colores a partir de la cual te gustaría crear un gradiente.
La siguiente es una lista de los modificadores Brush de gradiente disponibles y su resultado correspondiente:
Tipo de Brush de gradiente | Resultado |
---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
Nota: Para lograr una transición fluida entre colores, establece el último color como el color de inicio. |
|
Brush.radialGradient(colorList) |
Cambia la distribución de los colores con colorStops
Para personalizar la forma en que aparecen los colores en el gradiente, puedes ajustar el valor de colorStops
de cada uno. colorStops
se debe especificar como una fracción, entre 0 y 1. Los valores superiores a 1 harán que esos colores no se procesen como parte del gradiente.
Puedes configurar elementos stop a los colores para que tengan diferentes cantidades, como menos o más de un color:
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)) )
Los colores están dispersos en el desplazamiento proporcionado, según se define en el par colorStop
, menos amarillo que rojo y azul.
Repite un patrón con TileMode
Cada Brush de gradiente tiene la opción de establecer un TileMode
en él. Es posible que no notes el elemento TileMode
si no estableciste un inicio y un final para la gradiente, ya que llenará toda el área de manera predeterminada. Un elemento TileMode
solo dividirá el gradiente si el tamaño del área es mayor que el Brush.
En el siguiente código, se repetirá el patrón de gradiente 4 veces, ya que endX
se establece en 50.dp
y el tamaño se establece en 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 ) ) )
A continuación, se muestra una tabla en la que se detalla lo que hacen los diferentes modos de Tile para el ejemplo anterior de HorizontalGradient
:
TileMode | Salida |
---|---|
TileMode.Repeated : El borde se repite desde el último color hasta el primero. |
|
TileMode.Mirror : El borde se duplica desde el último color hasta el primero. |
|
TileMode.Clamp : El borde se fijó en el color final. Luego, se aplicará el color más cercano al resto de la región. |
|
TileMode.Decal : Renderiza solo hasta el tamaño de los límites. TileMode.Decal aprovecha el contenido transparente negro para hacer un muestreo del contenido fuera de los límites originales, mientras que TileMode.Clamp muestra el color del borde. |
TileMode
funciona de manera similar para los otros gradientes direccionales. La diferencia es la dirección en la que se repite.
Cambiar el tamaño del pincel
Si conoces el tamaño del área en la que se dibujará tu modificador Brush, puedes configurar la tarjeta endX
como vimos antes en la sección TileMode
. Si estás en un DrawScope
, puedes usar su propiedad size
para obtener el tamaño del área.
Si no conoces el tamaño del área de dibujo (por ejemplo, si Brush
está asignado a texto), puedes extender Shader
y usar el tamaño del área de dibujo en la función createShader
.
En este ejemplo, se divide el tamaño por 4 para repetir el patrón 4 veces:
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) )
También puedes cambiar el tamaño del modificador Brush de cualquier otro gradiente, como los radiales. Si no especificas un tamaño y un centro, el gradiente ocupará los límites completos del DrawScope
, y el centro del gradiente radial se establecerá de forma predeterminada en el centro de los límites del DrawScope
. Esto hace que el centro del gradiente radial aparezca como el centro de la dimensión más pequeña (ya sea de ancho o de altura):
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
Cuando se modifica el gradiente radial para establecer el tamaño del radio en la dimensión máxima, puedes ver que produce un mejor efecto de gradiente radial:
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 señalar que el tamaño real que se pasa a la creación del sombreador se determina desde el lugar en el que se invoca. De forma predeterminada, Brush
reasignará su Shader
internamente si el tamaño es diferente de la creación más reciente del Brush
, o bien si cambió algún objeto de estado en la creación del sombreador.
En el siguiente código, se crea el sombreador tres veces diferentes con tamaños diferentes, ya que el tamaño del área de dibujo cambia:
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) } } } )
Usa una imagen como un pincel
Para usar un objeto ImageBitmap como Brush
, carga la imagen como ImageBitmap
y crea un modificador Brush 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))
Brush se aplica a diferentes tipos de dibujos: un fondo, el texto y el lienzo. Esto da como resultado lo siguiente:
Ten en cuenta que ahora el texto también se procesa con el objeto ImageBitmap
para pintar los píxeles del texto.
Ejemplo avanzado: Pincel personalizado
Pincel RuntimeShader
de AGSL
AGSL ofrece un subconjunto de capacidades de sombreador GLSL. Los sombreadores se pueden escribir en AGSL y usar con un modificador Brush en Compose.
Para crear un Brush de sombreador, primero define la string de sombreador de 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()
El sombreador anterior toma dos colores de entrada, calcula la distancia desde la parte inferior izquierda (vec2(0, 1)
) del área de dibujo y realiza un mix
entre los dos colores según la distancia. Esto produce un efecto de gradiente.
Luego, crea el Brush de sombreador y establece los uniformes de resolution
: el tamaño del área de dibujo, y el color
y el color2
que deseas usar como entrada para tu gradiente:
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) ) }
Cuando lo ejecutes, verás lo siguiente en la pantalla:
Ten en cuenta que puedes hacer mucho más con sombreadores que con gradientes, ya que se trata de cálculos matemáticos. Para obtener más información sobre AGSL, consulta la documentación de AGSL.
Recursos adicionales
Para obtener más ejemplos de cómo usar Brush en Compose, consulta los siguientes recursos:
- Color de texto con Brush animado en Compose 🖌️
- Diseños y gráficos personalizados en Compose - Android Dev Summit 2022
- Ejemplo de JetLagged - Brush RuntimeShader
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Modificadores de gráficos
- Gráficos en Compose
- Estilo del texto