Многие приложения должны иметь возможность точно контролировать то, что отображается на экране. Это может быть просто размещение рамки или круга на экране в нужном месте, или это может быть тщательно продуманное расположение графических элементов в самых разных стилях.
Базовый рисунок с модификаторами и DrawScope
Основной способ нарисовать что-то собственное в Compose — использовать модификаторы, такие как Modifier.drawWithContent
, Modifier.drawBehind
и Modifier.drawWithCache
.
Например, чтобы нарисовать что-то позади компонуемого объекта, вы можете использовать модификатор drawBehind
чтобы начать выполнение команд рисования:
Spacer(
modifier = Modifier
.fillMaxSize()
.drawBehind {
// this = DrawScope
}
)
Если все, что вам нужно, это составной объект, который рисует, вы можете использовать составной объект Canvas
. Составной элемент Canvas
— это удобная оболочка Modifier.drawBehind
. Вы размещаете Canvas
в своем макете так же, как и любой другой элемент пользовательского интерфейса Compose. Внутри 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?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
. Однако если вы используете 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)
}
)
Нарисовать путь
Путь — это серия математических инструкций, результатом выполнения которых является рисунок. 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 — пример создания, показывающий, как нарисовать собственный график.
Пока рекомендаций нет.
Попытайтесь войти в свой аккаунт Google.