Многим приложениям необходимо иметь возможность точно контролировать то, что отображается на экране. Это может быть что-то незначительное, например, размещение прямоугольника или круга на экране в нужном месте, или это может быть сложная компоновка графических элементов во многих разных стилях.
Базовый рисунок с модификаторами и DrawScope
Основной способ нарисовать что-то пользовательское в Compose — использовать модификаторы, такие как Modifier.drawWithContent
, Modifier.drawBehind
и Modifier.drawWithCache
.
Например, чтобы нарисовать что-то позади вашего объекта, вы можете использовать модификатор drawBehind
, чтобы начать выполнять команды рисования:
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
Если вам нужен только компонуемый элемент, который рисует, вы можете использовать компонуемый элемент Canvas
. Компонуемый элемент Canvas
— это удобная оболочка вокруг Modifier.drawBehind
. Вы размещаете Canvas
в своем макете так же, как и любой другой элемент Compose UI. В Canvas
вы можете рисовать элементы с точным контролем над их стилем и расположением.
Все модификаторы рисования предоставляют DrawScope
, ограниченную среду рисования, которая поддерживает свое собственное состояние. Это позволяет вам устанавливать параметры для группы графических элементов. DrawScope
предоставляет несколько полезных полей, таких как size
, объект Size
, определяющий текущие размеры DrawScope
.
Чтобы нарисовать что-то, вы можете использовать одну из многочисленных функций рисования на DrawScope
. Например, следующий код рисует прямоугольник в верхнем левом углу экрана:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F drawRect( color = Color.Magenta, size = canvasQuadrantSize ) }

Дополнительную информацию о различных модификаторах рисования см. в документации по модификаторам графики .
Система координат
Чтобы нарисовать что-то на экране, вам нужно знать смещение ( x
и y
) и размер вашего элемента. Во многих методах рисования в DrawScope
положение и размер предоставляются значениями параметров по умолчанию . Параметры по умолчанию обычно размещают элемент в точке [0, 0]
на холсте и предоставляют size
по умолчанию, который заполняет всю область рисования, как в примере выше — вы можете видеть, что прямоугольник расположен вверху слева. Чтобы настроить размер и положение вашего элемента, вам нужно понимать систему координат в Compose.
Начало системы координат ( [0,0]
) находится в верхнем левом пикселе области рисования. x
увеличивается при перемещении вправо, а y
увеличивается при перемещении вниз.
![Сетка, показывающая систему координат, показывающую верхний левый угол [0, 0] и нижний правый угол [ширина, высота]](https://developer.android.google.cn/static/develop/ui/compose/images/graphics/introduction/compose_coordinate_system_drawing.png?authuser=3&hl=ru)
Например, если вы хотите нарисовать диагональную линию из правого верхнего угла области холста в левый нижний угол, вы можете использовать функцию DrawScope.drawLine()
и указать начальное и конечное смещение с соответствующими координатами x и y:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasWidth = size.width val canvasHeight = size.height drawLine( start = Offset(x = canvasWidth, y = 0f), end = Offset(x = 0f, y = canvasHeight), color = Color.Blue ) }
Базовые преобразования
DrawScope
предлагает преобразования, позволяющие изменить место и способ выполнения команд рисования.
Шкала
Используйте DrawScope.scale()
, чтобы увеличить размер операций рисования в несколько раз. Операции типа scale()
применяются ко всем операциям рисования в пределах соответствующей лямбды. Например, следующий код увеличивает scaleX
в 10 раз и scaleY
в 15 раз:
Canvas(modifier = Modifier.fillMaxSize()) { scale(scaleX = 10f, scaleY = 15f) { drawCircle(Color.Blue, radius = 20.dp.toPx()) } }

Переводить
Используйте DrawScope.translate()
для перемещения операций рисования вверх, вниз, влево или вправо. Например, следующий код перемещает рисунок на 100 пикселей вправо и на 300 пикселей вверх:
Canvas(modifier = Modifier.fillMaxSize()) { translate(left = 100f, top = -300f) { drawCircle(Color.Blue, radius = 200.dp.toPx()) } }

Повернуть
Используйте DrawScope.rotate()
для вращения операций рисования вокруг опорной точки. Например, следующий код поворачивает прямоугольник на 45 градусов:
Canvas(modifier = Modifier.fillMaxSize()) { rotate(degrees = 45F) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }

rotate()
, чтобы применить поворот к текущей области рисования, который поворачивает прямоугольник на 45 градусов.Вставка
Используйте DrawScope.inset()
для настройки параметров по умолчанию текущего DrawScope
, изменяя границы чертежа и преобразуя чертежи соответствующим образом:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F inset(horizontal = 50f, vertical = 30f) { drawRect(color = Color.Green, size = canvasQuadrantSize) } }
Этот код фактически добавляет отступы к командам рисования:

Множественные преобразования
Чтобы применить несколько преобразований к вашим рисункам, используйте функцию DrawScope.withTransform()
, которая создает и применяет одно преобразование, объединяющее все желаемые вами изменения. Использование withTransform()
более эффективно, чем вложенные вызовы отдельных преобразований, поскольку все преобразования выполняются вместе в одной операции, вместо Compose, которому нужно вычислять и сохранять каждое из вложенных преобразований.
Например, следующий код применяет к прямоугольнику как перемещение, так и поворот:
Canvas(modifier = Modifier.fillMaxSize()) { withTransform({ translate(left = size.width / 5F) rotate(degrees = 45F) }) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }

withTransform
, чтобы применить как поворот, так и перемещение, вращая прямоугольник и сдвигая его влево.Обычные операции рисования
Нарисовать текст
Для рисования текста в Compose обычно можно использовать Text
composable. Однако, если вы находитесь в DrawScope
или хотите нарисовать текст вручную с настройкой, можно использовать метод DrawScope.drawText()
.
Чтобы нарисовать текст, создайте TextMeasurer
с помощью rememberTextMeasurer
и вызовите drawText
с измерителем:
val textMeasurer = rememberTextMeasurer() Canvas(modifier = Modifier.fillMaxSize()) { drawText(textMeasurer, "Hello") }

Измерение текста
Рисование текста работает немного иначе, чем другие команды рисования. Обычно вы указываете команде рисования размер (ширину и высоту), чтобы нарисовать форму/изображение. В случае с текстом есть несколько параметров, которые управляют размером отображаемого текста, например, размер шрифта, шрифт, лигатуры и интервал между буквами.
С Compose вы можете использовать TextMeasurer
для получения доступа к измеренному размеру текста в зависимости от вышеуказанных факторов. Если вы хотите нарисовать фон позади текста, вы можете использовать измеренную информацию для получения размера области, которую занимает текст:
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixedWidth((size.width * 2f / 3f).toInt()), style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
Этот фрагмент кода создает розовый фон для текста:

Настройка ограничений, размера шрифта или любого свойства, которое влияет на измеренный размер, приводит к новому сообщенному размеру. Вы можете установить фиксированный размер как для width
, так и height
, и текст затем будет следовать установленному TextOverflow
. Например, следующий код отображает текст в ⅓ высоты и ⅓ ширины компонуемой области и устанавливает TextOverflow
в TextOverflow.Ellipsis
:
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixed( width = (size.width / 3f).toInt(), height = (size.height / 3f).toInt() ), overflow = TextOverflow.Ellipsis, style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
Текст теперь отображается в ограничениях с многоточием в конце:

TextOverflow.Ellipsis
с фиксированными ограничениями на измерение текста.Нарисовать изображение
Чтобы нарисовать ImageBitmap
с помощью DrawScope
, загрузите изображение с помощью ImageBitmap.imageResource()
, а затем вызовите drawImage
:
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { drawImage(dogImage) })

ImageBitmap
на холсте.Нарисуйте основные фигуры
На DrawScope
есть много функций рисования фигур. Чтобы нарисовать фигуру, используйте одну из предопределенных функций рисования, например drawCircle
:
val purpleColor = Color(0xFFBA68C8) Canvas( modifier = Modifier .fillMaxSize() .padding(16.dp), onDraw = { drawCircle(purpleColor) } )
API | Выход |
![]() | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() |
Нарисовать путь
Путь — это ряд математических инструкций, которые после выполнения приводят к рисованию. DrawScope
может рисовать путь с помощью метода DrawScope.drawPath()
.
Например, скажем, вы хотите нарисовать треугольник. Вы можете сгенерировать путь с помощью таких функций, как lineTo()
и moveTo()
используя размер области рисования. Затем вызовите drawPath()
с этим вновь созданным путем, чтобы получить треугольник.
Spacer( modifier = Modifier .drawWithCache { val path = Path() path.moveTo(0f, 0f) path.lineTo(size.width / 2f, size.height / 2f) path.lineTo(size.width, 0f) path.close() onDrawBehind { drawPath(path, Color.Magenta, style = Stroke(width = 10f)) } } .fillMaxSize() )

Path
в Compose. Доступ к объекту Canvas
С DrawScope
у вас нет прямого доступа к объекту Canvas
. Вы можете использовать DrawScope.drawIntoCanvas()
чтобы получить доступ к самому объекту Canvas
, для которого вы можете вызывать функции.
Например, если у вас есть пользовательский Drawable
, который вы хотите нарисовать на холсте, вы можете получить доступ к холсту и вызвать Drawable#draw()
, передав объект Canvas
:
val drawable = ShapeDrawable(OvalShape()) Spacer( modifier = Modifier .drawWithContent { drawIntoCanvas { canvas -> drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt()) drawable.draw(canvas.nativeCanvas) } } .fillMaxSize() )

Drawable
.Узнать больше
Для получения дополнительной информации о рисовании в Compose ознакомьтесь со следующими ресурсами:
- Графические модификаторы — узнайте о различных типах модификаторов рисования.
- Кисть — узнайте, как персонализировать прорисовку вашего контента.
- Пользовательские макеты и графика в Compose — Android Dev Summit 2022 — Узнайте, как создать собственный пользовательский интерфейс в Compose с помощью макетов и графики.
- Пример JetLagged — пример создания, показывающий, как нарисовать пользовательский график.
Рекомендовано для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Графические модификаторы
- Графика в Compose
- Линии выравнивания в Jetpack Compose