В Compose Brush описывает способ отрисовки объекта на экране: она определяет цвет (или цвета), которые будут отображаться в области рисования (например, круг, квадрат, контур). Существует несколько встроенных кистей, полезных для рисования, таких как 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 , а size — на 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 назначена тексту), вы можете расширить 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 brush
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.
Дополнительные ресурсы
Дополнительные примеры использования функции «Кисть» в Compose можно найти в следующих ресурсах:
- Анимированная кисть для раскрашивания текста в Compose 🖌️
- Пользовательская графика и макеты в Compose — Android Dev Summit 2022
- Пример JetLagged - Кисть RuntimeShader
Рекомендуем вам
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Модификаторы графики
- Графика в композиции
- Стиль текста







