Сетчатые градиенты создают сложные, многонаправленные цветовые переходы, используя двумерную сетку из отдельных участков. В отличие от линейных или радиальных градиентов, сетчатые градиенты плавно интерполируют цвета по сетке. Используйте сетчатые градиенты для создания плавных и органичных эстетических элементов в пользовательском интерфейсе.

Ключевые понятия
Для построения градиента сетки определите размеры сетки, вершины и цветовые переходы между точками:
- Размеры сетки: Сетка разбивается на фрагменты вдоль вертикальной и горизонтальной осей. Сетка из
rowsиcolumnsсодержит (строки+1)×(столбцы+1) вершин. Например, сетка 1×1 состоит из 4 вершин, образующих один фрагмент. - Нормализованные координаты: Все позиции вершин используют нормализованную систему координат, где
(0f, 0f)представляет верхний левый угол, а(1f, 1f)— нижний правый угол границ отрисовки. - Контрольные точки Безье (касательные): Каждая вершина содержит до четырех необязательных контрольных точек Безье. Эти касательные определяют кривизну ребер между соседними вершинами. Если вы используете
Offset.Unspecified, Compose автоматически определяет касательные для обеспечения плавных переходов между участками. Каждая ячейка сетки, образованная 4 вершинами вместе с их контрольными точками, генерирует участок Безье. - Интерполяция цвета: фреймворк вычисляет цвета между основными вершинами. Установите
hasBicubicColorвtrueдля интерполяции Катмулла-Рома , обеспечивающей более плавные изменения цвета, илиfalseдля билинейной интерполяции.
Рисуйте с помощью MeshGradientPainter
В Jetpack Compose используйте MeshGradientPainter для отрисовки градиента сетки. MeshGradientPainter рисует на холсте.
Создайте простой градиент сетки.
Для создания простого статического градиента сетки инициализируйте объект MeshGradientPainter , указав его размеры, и используйте функцию setVertex внутри блока конфигурации, чтобы расположить угловые точки и присвоить им цвета.
val rows = 1 val columns = 1 val gradientPainter = remember { MeshGradientPainter(rows, columns) { // Parameters: row, column, position, color setVertex(0, 0, Offset(0f, 0f), Color.Red) // Top-Left setVertex(0, 1, Offset(1f, 0f), Color.Blue) // Top-Right setVertex(1, 0, Offset(0f, 1f), Color.Green) // Bottom-Left setVertex(1, 1, Offset(1f, 1f), Color.Yellow) // Bottom-Right } } Box( modifier = modifier .aspectRatio(16/9f) .fillMaxWidth() .paint(gradientPainter) )

Используйте конкретные контрольные точки Безье.
По умолчанию генератор сетки выполняет сложные вычисления для обеспечения плавных переходов между элементами сетки. Однако вы можете явно настроить касательные к любой отдельной вершине, если хотите выборочно смещать, растягивать или резко сжимать определенные цветовые участки.
Смещение управляющих воздействий измеряется относительно положения основной вершины.
val customTangentPainter = remember { MeshGradientPainter(rows = 1, columns = 1) { // Tweak the top-left vertex to curve outwards to the right and bottom setVertex( row = 0, column = 0, position = Offset(0f, 0f), color = Color.Magenta, rightControlPoint = Offset(0.4f, 0.1f), bottomControlPoint = Offset(0.1f, 0.4f) ) // Other points can remain unspecified to use default inferred fallback tangents setVertex(0, 1, Offset(1f, 0f), Color.Cyan) setVertex(1, 0, Offset(0f, 1f), Color.Blue) setVertex(1, 1, Offset(1f, 1f), Color.Black) } } Box( modifier = modifier .aspectRatio(16/9f) .fillMaxWidth() .paint(customTangentPainter) )

Создавайте сложные сетки
В этом примере показана сетка 3 на 3, то есть необходимо указать 16 точек, при этом средние точки заданы с различными смещениями:
val points = remember { listOf( Offset(0.0f, 0.0f), Offset(0.3f, 0.0f), Offset(0.7f, 0.0f), Offset(1.0f, 0.0f), Offset(0.0f, 0.3f), Offset(0.2f, 0.4f), Offset(0.7f, 0.2f), Offset(1.0f, 0.3f), Offset(0.0f, 0.7f), Offset(0.3f, 0.8f), Offset(0.7f, 0.6f), Offset(1.0f, 0.7f), Offset(0.0f, 1.0f), Offset(0.3f, 1.0f), Offset(0.7f, 1.0f), Offset(1.0f, 1.0f) ) } val gradientPainter = remember { MeshGradientPainter(rows = 3, columns = 3) { // Row 0 setVertex(0, 0, points[0], yellow) setVertex(0, 1, points[1], orange) setVertex(0, 2, points[2], yellow) setVertex(0, 3, points[3], purple) // Row 1 setVertex(1, 0, points[4], pink) setVertex(1, 1, points[5], yellow) setVertex(1, 2, points[6], pink) setVertex(1, 3, points[7], purple) // Row 2 setVertex(2, 0, points[8], indigo) setVertex(2, 1, points[9], pink) setVertex(2, 2, points[10], purple) setVertex(2, 3, points[11], indigo) // Row 3 setVertex(3, 0, points[12], purple) setVertex(3, 1, points[13], indigo) setVertex(3, 2, points[14], pink) setVertex(3, 3, points[15], yellow) } } Box( modifier = modifier.padding(32.dp) .aspectRatio(16 / 9f) .fillMaxWidth() .paint(gradientPainter) // ... )

Анимировать градиент сетки
Поскольку лямбда-параметр block MeshGradientPainter выполняется внутри DrawScope , он может считывать и отслеживать изменяемое состояние. Вы можете анимировать положение или цвет во времени без перераспределения шейдеров или растровых изображений.
val infiniteTransition = rememberInfiniteTransition(label = "meshMovement") val animatedOffset by infiniteTransition.animateFloat( initialValue = -0.1f, targetValue = 0.1f, animationSpec = infiniteRepeatable( animation = tween(2500, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "offset" ) val coral = Color(255, 90, 90) val peach = Color(255, 139, 90) val amber = Color(255, 169, 90) val sunshine = Color(255, 212, 90) val indigo = Color(0xFF5856D6) val pink = Color(0xFFFF2D55) val gradientPainter = remember { MeshGradientPainter(rows = 3, columns = 3) { // Row 0 setVertex(0, 0, Offset(0.0f, 0.0f), indigo) setVertex(0, 1, Offset(0.3f, 0.0f), peach) setVertex(0, 2, Offset(0.7f, 0.0f), amber) setVertex(0, 3, Offset(1.0f, 0.0f), sunshine) // Row 1 setVertex(1, 0, Offset(0.0f, 0.3f), pink) setVertex(1, 1, Offset(0.2f, 0.4f) + Offset(animatedOffset, animatedOffset), coral) setVertex(1, 2, Offset(0.7f, 0.2f) + Offset(animatedOffset, animatedOffset), peach) setVertex(1, 3, Offset(1.0f, 0.3f), indigo) // Row 2 setVertex(2, 0, Offset(0.0f, 0.7f), coral) setVertex(2, 1, Offset(0.3f, 0.8f) + Offset(animatedOffset, 0f), pink) setVertex(2, 2, Offset(0.7f, 0.6f) + Offset(animatedOffset, 0f), sunshine) setVertex(2, 3, Offset(1.0f, 0.7f), amber) // Row 3 setVertex(3, 0, Offset(0.0f, 1.0f), sunshine) setVertex(3, 1, Offset(0.3f, 1.0f), amber) setVertex(3, 2, Offset(0.7f, 1.0f), pink) setVertex(3, 3, Offset(1.0f, 1.0f), indigo) } } Box( modifier = modifier.padding(32.dp) .safeContentPadding() .aspectRatio(16 / 9f) .fillMaxWidth() .paint(gradientPainter) )