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 최적화는 사용자 지정 그래픽에서 60FPS를 달성하기 위한 핵심 방법 중 하나입니다.
모든 Paint 설정은 여러 범주로 나뉩니다: 색상 및 투명도, 선 및 채우기 스타일, 텍스트 및 글꼴 처리, 흐림 및 마스킹 효과. 각 범주는 독립적인 세터 메서드로 제어되며 어떤 조합으로도 결합할 수 있습니다. 이로 인해 Paint API는 유연하지만 매개변수 상호 작용에 대한 이해가 필요합니다 — 예를 들어, 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 메서드는 FILL(채우기), STROKE(윤곽선) 또는 FILL_AND_STROKE(둘 다)의 세 가지 Paint.Style 값 중 하나를 받습니다. 스타일 선택은 모양이 실루엣, 와이어프레임 또는 내부 채우기가 있는 윤곽선으로 표시될지 결정합니다. 기본적으로 Paint는 FILL 스타일로 생성됩니다.
setStrokeWidth는 STROKE 모드의 선 두께를 픽셀 단위로 설정합니다. 둥근 모양과 둥근 모서리의 경우 setStrokeCap(Cap.ROUND, BUTT, SQUARE) 및 setStrokeJoin(Join.MITER, ROUND, BEVEL)을 사용합니다. 이러한 매개변수는 사용자 지정 View에서 그래프, 경로 및 테두리의 모양에 중요한 영향을 미칩니다.
1픽셀 미만의 두께에서는 선이 서브픽셀 정밀도로 그려지지만 저밀도 화면에서는 흐릿하게 보일 수 있습니다. Android Retina 디스플레이의 경우 TypedValue.applyDimension을 사용하여 dp를 픽셀로 변환합니다. 2dp의 두께는 대부분의 장치에서 선명한 가시성을 제공합니다.
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는 Paint 인스턴스에서 setTypeface를 사용하여 설정됩니다.
setTextSize 메서드는 글꼴 크기를 픽셀 단위로 설정합니다. 다양한 화면에서 올바르게 표시하려면 TypedValue.applyDimension을 사용하여 크기를 sp로 지정하는 것이 좋습니다. 줄 간격은 수평 늘이기를 위해 setTextScaleX를 사용하여 조정되며 Canvas.drawText를 통해 내장된 여러 줄 텍스트로 지원됩니다.
텍스트 정렬을 위해 setTextAlign이 LEFT, CENTER 및 RIGHT 값과 함께 사용됩니다. CENTER의 경우 drawText에 전달된 x 좌표가 텍스트의 중심점이 됩니다 — 이는 아이콘 아래의 레이블과 도형 내부의 제목에 특히 편리합니다. 정확한 위치 지정을 위해 텍스트의 너비와 경계 사각형을 반환하는 measureText 및 getTextBounds 메서드가 사용됩니다.
setUnderlineText 및 setStrikeThruText는 각각 밑줄과 취소선을 활성화합니다. 이러한 매개변수는 서체 수준에서 작동하며 이 Paint로 그려진 모든 텍스트에 적용됩니다. 동일한 문자열의 다른 부분을 유연하게 포맷하려면 설정이 다른 별도의 Paint 개체를 사용하거나 drawText 호출 간에 매개변수를 전환합니다.
| 메서드 | 목적 | 단위 |
|---|---|---|
| setTextSize | 글꼴 크기 | px (픽셀) |
| setTextAlign | 수평 정렬 | LEFT, CENTER, RIGHT |
| setTypeface | 글꼴 서체 | Typeface 객체 |
| setUnderlineText | 밑줄 | Boolean |
Shader는 그래디언트, 비트맵 텍스처 및 합성으로 도형을 채우기 위한 기본 클래스입니다. setShader 메서드를 통해 LinearGradient, RadialGradient, SweepGradient, BitmapShader 또는 ComposeShader의 하위 클래스 중 하나를 할당할 수 있습니다. Shader는 setColor로 설정된 색상을 대체하고 그려진 모든 요소에 적용됩니다.
LinearGradient는 직선을 따라 두 개 이상의 색상 사이에 부드러운 전환을 만듭니다. 생성자는 시작점과 끝점, 색상 배열 및 위치(타일 모드)를 받습니다. 타일 모드는 그래디언트 너머의 동작을 결정합니다: 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이 더 좋습니다. 재설정 후 모든 스타일 매개변수는 기본값을 취합니다.
onDraw 내에서 Paint를 생성하면 모든 프레임에서 할당이 발생하여 GC 중단으로 이어집니다. 모든 Paint 객체는 View 생성자에서 생성되어야 합니다. 세터를 통해 onDraw에서 매개변수를 변경하는 것은 안전하며 새 객체를 만들지 않습니다.
Android의 텍스트는 항상 투명한 배경으로 그려집니다. drawText 메서드는 배경 사각형 없이 글리프만 출력합니다. 텍스트 아래에 배경이 필요한 경우 먼저 별도의 Paint로 drawRoundRect를 사용하여 사각형을 그린 다음 텍스트를 그립니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.