Compose 中的 Brush
用于描述内容在屏幕上的绘制方式:它可以确定要在绘制区域(即圆形、方形、路径)中绘制的颜色。有一些内置 Brush 对绘制非常有用,例如 LinearGradient
、RadialGradient
或普通的 SolidColor
Brush。
Brush 可与 Modifier.background()
、TextStyle
或 DrawScope
绘制调用搭配使用,以将绘制样式应用于要绘制的内容。
例如,可以在 DrawScope
中应用横向渐变 Brush 以绘制圆形:
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
渐变 Brush
您可以使用许多内置渐变 Brush 来实现不同的渐变效果。借助这些 Brush,您可以指定颜色列表,以使用这些颜色创建渐变。
可用的渐变 Brush 及其对应输出的列表:
渐变 Brush 类型 | 输出 |
---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
注意:为了在不同颜色之间实现顺畅自然的过渡,请将最后一种颜色设为起始颜色。 |
|
Brush.radialGradient(colorList) |
使用 colorStops
更改颜色分布
如需自定义颜色在渐变中的显示方式,您可以调整每种颜色的 colorStops
值。指定 colorStops
时,应采用介于 0 到 1 之间的小数。值大于 1 会导致相应颜色无法渲染为渐变的组成部分。
您可以将各颜色停止点配置为具有不同的色量,例如让某种颜色少一些或多一些:
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)) )
颜色将按照所提供的偏移量(如 colorStop
对中所定义)分布,即黄色少于红色和蓝色。
使用 TileMode
重复图案
对于每个渐变 Brush,您都可以选择为其设置一个 TileMode
。如果您尚未设置渐变的起始值和结束值,则可能不会注意到 TileMode
,因为在默认情况下,它会填满整个区域。只有当区域大小大于 Brush 大小时,TileMode
才会在渐变中平铺。
以下代码会让渐变图案重复显示 4 次,因为 endX
已设置为 50.dp
且大小已设置为 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 ) ) )
下表详细说明了不同 Tile Mode 对上述 HorizontalGradient
示例的作用:
TileMode | 输出 |
---|---|
TileMode.Repeated :边缘按从最后一种颜色到第一种颜色的顺序重复。 |
|
TileMode.Mirror :边缘按从最后一种颜色到第一种颜色的顺序镜像。 |
|
TileMode.Clamp :边缘固定为最终的颜色。然后,它会将区域的剩余空间绘制为距离最近的颜色。 |
|
TileMode.Decal :仅在边界大小的范围内渲染。TileMode.Decal 利用透明的黑色对原始边界以外的内容进行采样,而 TileMode.Clamp 对边缘颜色进行采样。 |
对于其他方向的渐变,TileMode
的运作方式也是如此,区别在于重复显示沿哪个方向发生。
更改笔刷大小
如果您知道 Brush 要绘制的区域的具体大小,则可以按照 TileMode
部分中所述的方式设置平铺的 endX
。如果是在 DrawScope
中,则可以使用其 size
属性获取区域大小。
如果您不知道绘制区域的大小(例如,如果将 Brush
分配给文字),您可以扩展 Shader
并在 createShader
函数中利用绘制区域大小。
在以下示例中,用大小值除以 4 以使图案重复显示 4 次:
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) )
您还可以更改任何其他渐变的 Brush 大小,例如径向渐变。如果您未指定大小和中心,渐变将占据 DrawScope
的整个边界,径向渐变的中心也将默认设置为 DrawScope
边界的中心。这会导致径向渐变的中心显示为较小维度(宽度或高度)的中心:
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
如果将径向渐变更改为将半径大小设置为最大维度,您会发现这样一来,径向渐变的效果会更好:
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) )
值得注意的是,为创建着色器而传递的实际尺寸是由调用着色器的位置决定的。默认情况下,如果大小与上次创建 Brush
时不同,或者创建着色器时使用的状态对象发生更改,Brush
会在内部重新分配其 Shader
。
以下代码会随着绘制区域大小的变化,按照不同的大小创建 3 次着色器:
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) } } } )
使用图片作为 Brush
如需使用 ImageBitmap 作为 Brush
,请以 ImageBitmap
的形式加载相应图片,然后创建 ImageShader
Brush:
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 将应用于几种不同类型的绘制:背景、文字和画布。这会输出以下内容:
请注意,现在,系统在渲染相应文字时,同样会使用 ImageBitmap
来绘制文字的像素。
高级示例:自定义 Brush
AGSL RuntimeShader
笔刷
AGSL 提供了一部分 GLSL 着色器功能。着色器可以使用 AGSL 编写,并在 Compose 中与 Brush 搭配使用。
如需创建着色器 Brush,请先将着色器定义为 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()
以上着色器会接受两种输入颜色,计算与绘制区域左下角 (vec2(0, 1)
) 的距离,并根据此距离在这两种颜色之间执行 mix
。这样即可生成渐变效果。
然后,创建着色器 Brush,并设置 resolution
的 uniform,即绘制区域的大小,以及要用作自定义渐变色的输入的 color
和 color2
:
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) ) }
运行以上代码后,您会看到屏幕上将渲染以下内容:
值得注意的是,除了实现渐变以外,着色器还有许多其他用途,因为着色器的本质是基于数学的计算。如需详细了解 AGSL,请参阅 AGSL 文档。
其他资源
如需查看在 Compose 中使用 Brush 的更多示例,请参阅以下资源:
- 在 Compose 中为 Brush 文字着色添加动画效果 🖌️
- Compose 中的自定义图形和布局 - 2022 年 Android 开发者峰会
- JetLagged 示例 - RuntimeShader Brush
为您推荐
- 注意:当 JavaScript 处于关闭状态时,系统会显示链接文字
- 图形修饰符
- Compose 中的图形
- 设置文本样式