Brush
в Compose описывает, как объект отображается на экране: она определяет цвет(ы), которые отображаются в области рисования (например, круг, квадрат, контур). Существует несколько встроенных кистей, полезных для рисования, например, LinearGradient
, RadialGradient
или простая кисть SolidColor
.
Кисти можно использовать с вызовами отрисовки Modifier.background()
, TextStyle
или DrawScope
для применения стиля рисования к рисуемому содержимому.
Например, горизонтальную градиентную кисть можно применить для рисования круга в DrawScope
:
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(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
Для каждой градиентной кисти можно задать режим TileMode
. Вы можете не заметить TileMode
, если не задали начало и конец градиента, поскольку по умолчанию он заполняет всю область. 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 ) ) )
Ниже приведена таблица, в которой подробно описано, что делают различные режимы плитки для приведенного выше примера HorizontalGradient
:
TileMode | Выход |
---|---|
TileMode.Repeated : Край повторяется от последнего цвета до первого. | ![]() |
TileMode.Mirror : Край зеркально отражается от последнего цвета к первому. | ![]() |
TileMode.Clamp : край фиксируется на конечном цвете. Затем остальная часть области окрашивается в наиболее близкий цвет. | ![]() |
TileMode.Decal : визуализировать только до размера границ. TileMode.Decal использует прозрачный чёрный цвет для выборки содержимого за пределами исходных границ, тогда как TileMode.Clamp выбирает цвет края. | ![]() |
TileMode
работает аналогичным образом для других направленных градиентов, разница заключается в направлении, в котором происходит повторение.
Изменить размер кисти
Если вы знаете размер области, в которой будет прорисована кисть, вы можете задать значение endX
тайла, как мы видели выше в разделе TileMode
. Если вы находитесь в DrawScope
, вы можете использовать его свойство size
для получения размера области.
Если вы не знаете размер области рисования (например, если Brush
назначена на Text), вы можете расширить 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) )

Вы также можете изменить размер кисти любого другого градиента, например, радиального. Если размер и центр не указаны, градиент будет занимать все границы области 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
перераспределяет свой Shader
внутри себя, если его размер отличается от размера, который был при последнем создании Brush
, или если изменился объект состояния, использованный при создании шейдера.
Следующий код создает шейдер три раза с разными размерами, поскольку изменяется размер области рисования:
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) } } } )
Использовать изображение как кисть
Чтобы использовать ImageBitmap в качестве Brush
, загрузите изображение как ImageBitmap
и создайте кисть 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))
Кисть применяется к нескольким типам рисунков: фону, тексту и холсту. Результатом являются:

Обратите внимание, что текст теперь также визуализируется с использованием ImageBitmap
для закраски пикселей текста.
Расширенный пример: пользовательская кисть
Кисть AGSL RuntimeShader
AGSL предлагает подмножество возможностей шейдеров GLSL . Шейдеры можно писать на AGSL и использовать с кистью в Compose.
Чтобы создать кисть шейдера, сначала определите шейдер как строку шейдера 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
два цвета на основе этого расстояния. Это создаёт эффект градиента.
Затем создайте Shader Brush и задайте параметры resolution
— размер области рисования, а также 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.
Дополнительные ресурсы
Дополнительные примеры использования Brush в Compose можно найти на следующих ресурсах:
- Анимированная кисть Раскрашивание текста в Compose 🖌️
- Пользовательская графика и макеты в Compose — Android Dev Summit 2022
- Пример JetLagged — кисть RuntimeShader
Рекомендовано для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Графические модификаторы
- Графика в Compose
- Стиль текста