Paint — основний клас для стилізації графіки в Android Canvas. Він визначає колір, товщину лінії, стиль заливки, ефекти розмиття та шрифти для всіх операцій малювання в інтерфейсі користувача. Без Paint Canvas не може застосовувати візуальні атрибути до елементів, що відображаються. За даними Google Developers, 2026, Paint підтримує до 42 стильових параметрів, включаючи градієнти та текстури.
Головне
Paint — клас з пакета android.graphics, який зберігає всі налаштування стилів для операцій малювання на Canvas. Він не містить самих пікселів, а визначає як саме виглядатимуть лінії, фігури та текст. Кожен виклик методу Canvas — drawCircle, drawLine, drawText — використовує Paint для інтерпретації візуального представлення.
Paint працює за принципом конфігураційного об'єкта. Ви створюєте екземпляр, задаєте параметри один раз і перевикористовуєте його для безлічі операцій. Це мінімізує кількість алокацій у циклі відтворення та підвищує продуктивність до 30% порівняно зі створенням нового об'єкта кожен кадр. За даними Google I/O 2024, оптимізація Paint — одна з ключових практик для досягнення 60 FPS у кастомній графіці.
Всі налаштування Paint поділяються на кілька категорій: колір і прозорість, стиль обведення та заливки, робота з текстом і шрифтами, ефекти розмиття та маскування. Кожна категорія керується окремими методами-сетерами, які можна комбінувати в будь-яких поєднаннях. Це робить API Paint гнучким, але потребує розуміння взаємодії параметрів — наприклад, Style впливає на те, чи застосовується Shader до контуру чи до заливки.
setColor — основний метод задання кольору для всіх наступних операцій малювання. Колір передається у форматі ARGB (32-бітне ціле число), де старші 8 біт — альфа-канал, а інші — червоний, зелений та синій канали. Значення 0xFF0000FF задає непрозорий синій колір.
Метод setAlpha дозволяє незалежно регулювати прозорість в діапазоні від 0 (повністю прозорий) до 255 (повністю непрозорий). На відміну від вказівки альфи в setColor, цей метод не впливає на вже встановлений колірний тон — він лише модулює загальну прозорість. Це зручно для анімації появи та зникнення елементів без зміни кольору.
ColorFilter — механізм трансформації кольору кожного пікселя перед відтворенням. Підтримуються три режими: LightingColorFilter для множення та додавання каналів, PorterDuffColorFilter для накладання з використанням режимів змішування та ColorMatrixColorFilter для повноцінної матричної трансформації. ColorFilter застосовується після Shader, але до виведення на Canvas.
Для більшості завдань достатньо стандартного setColor або setAlpha. ColorFilter використовується переважно для ефектів знебарвлення, сепії або інвертування. Застосування ColorFilter збільшує навантаження на GPU, тому Google рекомендує мінімізувати його використання в циклічних викликах onDraw.
| Метод | Опис | Продуктивність |
|---|---|---|
| setColor | Встановлює колір в ARGB | O(1) — без накладних витрат |
| setAlpha | Регулює прозорість (0-255) | O(1) — легкова операція |
| setColorFilter | Застосовує кольорокорекцію | O(n) — впливає на FPS при частих викликах |
Метод setStyle приймає одне з трьох значень Paint.Style: FILL (заливка), STROKE (контур) або FILL_AND_STROKE (одночасно). Від вибору стилю залежить, чи буде фігура відображатися як силует, як каркас або як контур з внутрішньою заливкою. За замовчуванням Paint створюється зі стилем FILL.
setStrokeWidth задає товщину лінії в пікселях для режиму STROKE. Для округлених фігур і скруглених кутів використовуються setStrokeCap (Cap.ROUND, BUTT, SQUARE) та setStrokeJoin (Join.MITER, ROUND, BEVEL). Ці параметри критично впливають на зовнішній вигляд графіків, шляхів і рамок у кастомних View.
При товщині менше 1 пікселя лінія малюється з субпіксельною точністю, але може виглядати розмитою на екранах з низькою щільністю. Для Retina-дисплеїв Android використовуйте TypedValue.applyDimension для перерахунку dp в пікселі. Товщина в 2 dp забезпечує чітку видимість на більшості пристроїв.
DashPathEffect створює пунктирні та штрихпунктирні лінії. Конструктор приймає масив довжин чергуючих штрихів і пропусків та початкове зміщення фази. Наприклад, масив [10, 5, 3, 5] створює лінію з довгим штрихом, коротким пробілом, коротким штрихом і пробілом. Цей ефект активно застосовується в графічних редакторах та інструментах для анотацій.
Об'єднання стилів здійснюється через setPathEffect. Комбінуючи CornerPathEffect, DashPathEffect та DiscretePathEffect, можна створювати складні візуальні лінії для діаграм, підписів і декоративних елементів. Кожен PathEffect обчислюється на CPU, тому в анімаціях з великою кількістю ліній рекомендується заздалегідь растеризувати ефект в Bitmap.
val paint = Paint().apply {
setStyle(Paint.Style.STROKE)
setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics))
setPathEffect(DashPathEffect(floatArrayOf(10f, 5f, 3f, 5f), 0f))
}
Typeface — об'єкт, що визначає гарнітуру шрифту для всіх текстових операцій Paint. Android підтримує системні шрифти (Roboto, Noto Sans), кастомні шрифти з ресурсів assets та завантажувані шрифти через Downloadable Fonts API. Встановлення Typeface здійснюється через setTypeface на екземплярі Paint.
Метод setTextSize задає розмір шрифту в пікселях. Для коректного відображення на різних екранах рекомендується вказувати розмір в sp за допомогою TypedValue.applyDimension. Міжрядковий інтервал регулюється setTextScaleX для горизонтального розтягнення та підтримується вбудованою підтримкою багаторядкового тексту через Canvas.drawText.
Для вирівнювання тексту використовується setTextAlign зі значеннями LEFT, CENTER та RIGHT. При CENTER координата x, передана в drawText, стає центральною точкою тексту — це особливо зручно для підписів під іконками та заголовків всередині фігур. Для точного позиціонування застосовуються методи measureText та getTextBounds, що повертають ширину та прямокутник меж тексту.
setUnderlineText та setStrikeThruText вмикають підкреслення та закреслення відповідно. Ці параметри працюють на рівні гарнітури та застосовуються до всього тексту, що малюється даним Paint. Для гнучкого форматування різних частин одного рядка використовуйте окремі об'єкти Paint з різними налаштуваннями або перемикайте параметри між викликами drawText.
| Метод | Призначення | Одиниці виміру |
|---|---|---|
| setTextSize | Розмір шрифту | px (пікселі) |
| setTextAlign | Горизонтальне вирівнювання | LEFT, CENTER, RIGHT |
| setTypeface | Гарнітура шрифту | Typeface object |
| setUnderlineText | Підкреслення | Boolean |
Shader — базовий клас для заповнення фігур градієнтами, растровими текстурами та композитингами. Через метод setShader можна призначити будь-який з підкласів: LinearGradient, RadialGradient, SweepGradient, BitmapShader або ComposeShader. Shader заміщує колір, заданий setColor, і застосовується до всіх елементів, що малюються.
LinearGradient створює плавний перехід між двома або більше кольорами вздовж прямої лінії. Конструктор приймає початкову та кінцеву точки, масив кольорів та їх позиції (tile mode). Tile mode визначає поведінку за межами градієнта: CLAMP (повтор останнього кольору), MIRROR (відображення) або REPEAT (повтор). LinearGradient активно застосовується для фонових заливок та прогрес-барів.
RadialGradient створює круговий градієнт від центру до країв. Центр задається координатами, радіус визначає область переходу кольорів. Зовнішній вигляд радіального градієнта нагадує освітлення від точкового джерела — це часто використовується для імітації світіння кнопок та підсвітки активних елементів.
BlurMaskFilter додає ефект розмиття навколо елементів, що малюються. Конструктор приймає радіус розмиття та стиль: NORMAL (розмиття за всіма напрямками), INNER (тільки всередині фігури), OUTER (тільки зовні) та SOLID (фігура + зовнішнє розмиття). BlurMaskFilter працює з будь-якими типами Paint і часто використовується для створення тіней та ефектів світіння.
val paint = Paint().apply {
setShader(LinearGradient(
0f, 0f, width, height,
intArrayOf(Color.RED, Color.BLUE),
null,
Shader.TileMode.CLAMP
))
setMaskFilter(BlurMaskFilter(20f, BlurMaskFilter.Blur.NORMAL))
}
Розглянемо повний приклад кастомного View, який малює коло з градієнтною заливкою та текстовим підписом. Комбінація Paint та Canvas дозволяє створювати складну графіку з мінімальним кодом. Всі налаштування винесені в ініціалізацію для продуктивності.
class GradientCircleView@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private val fillPaint = Paint().apply {
setShader(RadialGradient(
0f, 0f, 200f,
intArrayOf(Color.CYAN, Color.DKGRAY),
null, Shader.TileMode.CLAMP
))
}
private val textPaint = Paint().apply {
setColor(Color.WHITE)
setTextSize(TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 24f, resources.displayMetrics
))
setTextAlign(Paint.Align.CENTER)
setMaskFilter(BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL))
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(200f, 200f, 150f, fillPaint)
canvas.drawText("Градієнт", 200f, 210f, textPaint)
}
}
В коді використовуються два окремі об'єкти Paint: один для заливки кола радіальним градієнтом, другий для тексту з тінню через BlurMaskFilter. Такий поділ дозволяє незалежно налаштовувати параметри та уникати скидання стану Paint між відтворенням різних типів елементів. Для підвищення продуктивності обидва Paint створюються в конструкторі, а не в onDraw.
При масштабуванні на пристроях з різною щільністю пікселів значення розмірів повинні бути перераховані через TypedValue.applyDimension. Без цього текст та градієнт виглядатимуть по-різному на mdpi та xxxhdpi екранах. В прикладі розмір тексту задається в sp, що автоматично адаптується до системних налаштувань розміру шрифту користувача.
Часто задавані питання
Canvas виконує операції малювання (drawCircle, drawLine), а Paint визначає стиль — колір, товщину, шрифт. Canvas не містить налаштувань стилю, тому без Paint будь-яка операція малювання не має візуального ефекту.
Так, один об'єкт Paint можна перевикористовувати для безлічі фігур. Рекомендується створювати окремі Paint для різних типів елементів (фон, текст, контур), але не для кожної фігури — це знижує навантаження на збирач сміття.
Метод reset повертає Paint в стан, еквівалентний новому екземпляру. Альтернативно можна використати set з прапорцем, але reset є кращим для читання коду. Після скидання всі стильові параметри приймають значення за замовчуванням.
Створення Paint всередині onDraw викликає алокацію в кожному кадрі, що призводить до GC-фризів. Всі об'єкти Paint повинні створюватися в конструкторі View. Зміна параметрів в onDraw через сетери безпечна і не створює нових об'єктів.
Текст в Android завжди малюється з прозорим фоном. Метод drawText виводить тільки гліфи, без фонового прямокутника. Якщо потрібен фон під текстом, спочатку намалюйте прямокутник через drawRoundRect з окремим Paint, а потім текст.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також