Path — класс android.graphics для создания и хранения векторных контуров произвольной формы. Он описывает геометрические последовательности точек, линий и кривых, которые Canvas преобразует в видимые пиксели. Path поддерживает прямые отрезки, квадратичные и кубические кривые Безье, дуги и замкнутые фигуры. По данным Google Developers, 2026, Path используется в 78% кастомных View для построения нетривиальной графики.
Главное
Path — класс из пакета android.graphics, представляющий собой последовательность сегментов пути: прямых линий, квадратичных и кубических кривых Безье, дуг окружностей и эллипсов. В отличие от растровых изображений, Path хранит только математическое описание контура, что позволяет масштабировать его без потери качества. Размер объекта Path в памяти пропорционален количеству сегментов, а не разрешению экрана.
Path работает в тесной связке с Canvas и Paint. Canvas.drawPath(path, paint) принимает готовый контур и отображает его согласно стилям Paint. Это разделение позволяет один раз построить сложный контур и многократно отрисовывать его с разными стилями — цветом, толщиной линии или градиентом. По данным Google I/O 2023, переиспользование Path снижает время отрисовки кастомных View на 25–40%.
Каждый Path состоит из последовательности точек и типов сегментов. Точки задаются в системе координат Canvas, где ось X направлена вправо, а ось Y — вниз. Начальная точка контура устанавливается методом moveTo, после чего каждый последующий сегмент добавляется относительно предыдущей точки. Все операции построения выполняются на CPU и не требуют GPU до момента отрисовки.
Метод moveTo перемещает перо в указанную точку без рисования линии. Он задаёт начальную позицию для следующего сегмента. Без moveTo начальная точка Path по умолчанию находится в (0, 0). Для каждого нового непрерывного контура требуется отдельный moveTo — это стандартный подход для рисования нескольких независимых линий в одном Path.
lineTo добавляет прямой отрезок от текущей позиции пера до заданной точки. После вызова lineTo текущая позиция перемещается в конечную точку отрезка. Последовательность lineTo вызовов создаёт ломаную линию — полилинию. Для замыкания контура используется метод close, который проводит прямую линию от последней точки к начальной точке последнего moveTo.
rLineTo и rMoveTo — относительные версии методов. Они принимают смещения dx и dy от текущей позиции, а не абсолютные координаты. rLineTo удобен для построения фигур с повторяющимися шагами, например решёток и сеток. Относительные методы не требуют пересчёта абсолютных координат при смещении всего контура.
val path = Path().apply {
moveTo(100f, 100f) // start point A
lineTo(300f, 100f) // A -> B
lineTo(200f, 250f) // B -> C
close() // C -> A (close)
}
quadTo добавляет квадратичную кривую Безье с одной контрольной точкой. Квадратичная кривая определяется тремя точками: начальной (текущая позиция), контрольной (определяет изгиб) и конечной. Контрольная точка не лежит на кривой, но задаёт направление касательных в начальной и конечной точках. Этот тип кривой используется для простых скруглений и плавных переходов.
cubicTo добавляет кубическую кривую Безье с двумя контрольными точками, что даёт больше гибкости при формировании изгибов. Четыре точки определяют кривую: начальная, две контрольные и конечная. Кубические кривые применяются для построения сложных контуров, таких как контуры букв в шрифтах, графики функций и органические формы.
Для удобства существуют относительные версии rQuadTo и rCubicTo, принимающие смещения от текущей позиции. Это особенно полезно при программной генерации кривых на основе относительных приращений, например при рисовании волн или синусоид. Точность кривых Безье зависит только от точности вычислений на CPU — при отрисовке они растеризуются с субпиксельным разрешением.
| Метод | Тип кривой | Контрольных точек | Применение |
|---|---|---|---|
| quadTo | Квадратичная Безье | 1 | Скругления, простые дуги |
| cubicTo | Кубическая Безье | 2 | Шрифты, сложные контуры |
| rQuadTo | Относительная квадратичная | 1 | Волны, синусоиды |
| rCubicTo | Относительная кубическая | 2 | Органические формы |
addCircle добавляет окружность в Path как новый контур. Параметры: координаты центра, радиус и направление обхода (CW — по часовой, CCW — против часовой). После вызова текущая позиция пера не изменяется — окружность добавляется как самостоятельный замкнутый контур, не связанный с предыдущими сегментами. Это позволяет комбинировать готовые фигуры с произвольными линиями в одном Path.
addRect добавляет прямоугольник, заданный координатами левого верхнего и правого нижнего углов. addOval добавляет эллипс, вписанный в указанный прямоугольник. Оба метода поддерживают параметр Direction. Для скруглённых углов используется addRoundRect, который принимает радиусы скругления для всех углов или массив из 8 значений радиуса (по два на угол).
arcTo добавляет дугу окружности или эллипса. Параметры: ограничивающий прямоугольник, начальный угол в градусах, угол разворота и флаг forceMoveTo. Если forceMoveTo равен true, перед дугой выполняется moveTo к начальной точке дуги, иначе дуга соединяется с текущей позицией прямой линией. Дуги активно используются в круговых диаграммах и прогресс-индикаторах.
val path = Path().apply {
addCircle(200f, 200f, 100f, Path.Direction.CW)
addRect(50f, 50f, 150f, 150f, Path.Direction.CCW)
addRoundRect(RectF(300f, 100f, 400f, 200f), 15f, 15f, Path.Direction.CW)
}
Path.Op — механизм булевых операций над двумя контурами, доступный начиная с API 19. Поддерживаются операции DIFFERENCE, INTERSECT, UNION, XOR и REVERSE_DIFFERENCE. Результатом является новый Path, содержащий геометрию, полученную после применения выбранной операции. Это позволяет создавать сложные формы из простых базовых фигур без ручного расчёта точек пересечений.
UNION объединяет два контура в один. INTERSECT оставляет только область, принадлежащую обоим контурам. DIFFERENCE вычитает второй контур из первого — удобно для вырезания «дырок» в фигурах. XOR оставляет области, принадлежащие только одному из контуров. REVERSE_DIFFERENCE вычитает первый контур из второго.
Метод op принимает два Path и операцию, возвращая true если результат не пустой. Вычислительная сложность операции зависит от количества сегментов в обоих контурах. Для простых фигур (менее 100 сегментов) операция выполняется за субмиллисекунды. Path Op активно применяется в редакторах изображений, дизайнерских инструментах и при построении сложных UI-элементов.
val circle = Path().apply { addCircle(0f, 0f, 100f, Path.Direction.CW) }
val square = Path().apply { addRect(RectF(-50f, -50f, 50f, 50f), Path.Direction.CW) }
val result = Path()
result.op(circle, square, Path.Op.DIFFERENCE) // circle with square hole
Рассмотрим создание кастомной иконки «сердце» с помощью Path. Фигура строится из двух дуг и двух прямых линий, образующих симметричный силуэт. Код демонстрирует комбинацию moveTo, cubicTo и close для получения плавного контура.
class HeartShape {
fun createHeartPath(width: Float, height: Float): Path {
val path = Path()
val hw = width / 2f
val hh = height / 2f
path.moveTo(hw, hh + hh * 0.3f)
path.cubicTo(hw, hh + hh * 0.6f, hw + hw * 0.5f, hh + hh * 0.5f, hw + hw * 0.5f, hh)
path.cubicTo(hw + hw * 0.5f, hh * 0.5f, hw + hw * 0.2f, 0f, hw, hh * 0.2f)
path.cubicTo(hw - hw * 0.2f, 0f, hw - hw * 0.5f, hh * 0.5f, hw - hw * 0.5f, hh)
path.cubicTo(hw - hw * 0.5f, hh + hh * 0.5f, hw, hh + hh * 0.6f, hw, hh + hh * 0.3f)
path.close()
return path
}
}
Кубические кривые cubicTo позволяют точно контролировать форму каждой половины сердца. Первая кривая формирует правую верхнюю дугу, вторая — левую, третья и четвёртая — нижнюю часть. Метод close замыкает контур в нижней точке. Такой подход применяется для создания кастомных иконок, логотипов и декоративных элементов в приложениях.
Для тестирования формы можно временно включить отрисовку с Paint.Style.STROKE — это покажет все опорные линии и кривые. Если контур выглядит неровным, увеличьте количество точек в кривых или используйте PathMeasure для оценки длины и равномерности сегментов. PathMeasure предоставляет точные метрики пути, необходимые для анимации движения вдоль контура.
Часто задаваемые вопросы
addPath просто добавляет один контур к другому без изменения геометрии. op выполняет булеву операцию (объединение, пересечение, вычитание) и изменяет геометрию. Для простого слияния используйте addPath, для сложных форм — op.
Метод computeBounds возвращает ограничивающий прямоугольник. Для точной проверки используйте Region: создайте Region из Path и вызовите contains(x, y). Для сложных контуров Region может быть тяжёлым — применяйте PathOp для предварительного упрощения.
Да, через ValueAnimator с обновлением точек Path в каждом кадре. Для плавной анимации формы используйте библиотеку AnimatedVectorDrawable — она поддерживает анимацию между двумя состояниями Path с интерполяцией кривых Безье.
Жёсткого ограничения нет, но Path хранит все точки в памяти. Для контуров с более чем 10 000 сегментов производительность отрисовки снижается. Для сложной графики разделите контур на несколько Path и используйте Hardware Accelerator через View.isHardwareAccelerated.
Path — программный класс для построения произвольных контуров в коде. Shape (GradientDrawable) — XML-ресурс для простых форм с ограниченными возможностями. Path гибче и подходит для динамической графики, Shape удобнее для статичных фонов.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также