onDraw() é um método chave da classe View no Android SDK responsável por desenhar o conteúdo de uma visualização. O sistema chama onDraw em cada atualização da aparência da View — durante a renderização inicial, após invalidate() ou quando as dimensões mudam. De acordo com a documentação do Android Developers (2026), sobrescrever onDraw usando a Canvas API é a principal forma de criar gráficos personalizados em aplicativos Android.
Principais pontos
onDraw(Canvas canvas) é um método protected da classe android.view.View, que o sistema Android chama quando uma View precisa ser desenhada. O desenvolvedor sobrescreve este método para implementar desenho personalizado: formas, texto, imagens, gradientes, animações. O parâmetro Canvas é uma superfície gráfica na qual todas as operações de desenho são executadas.
A assinatura do método é simples: override fun onDraw(canvas: Canvas). Dentro deste método, não se deve criar novos objetos (para evitar a sobrecarga do GC), realizar operações longas ou trabalhar com a rede. View também inclui o método onDraw em seu ciclo de vida entre onLayout (cálculo de posições) e dispatchDraw (desenho de elementos filhos).
Ao herdar de View, sobrescrever onDraw é obrigatório para exibir qualquer conteúdo — caso contrário, a View ficará vazia. Ao herdar de subclasses existentes (TextView, ImageView, Button), sobrescrever onDraw é opcional e usado para adicionar gráficos extras sobre o conteúdo padrão.
Android realiza o desenho da View em uma sequência estritamente definida chamada a partir da thread principal (UI thread). O ciclo completo consiste em três fases: measure (medição de dimensões), layout (cálculo de posições) e draw (renderização). onDraw faz parte da fase draw e é chamado após as dimensões e a posição da View já terem sido determinadas.
draw(Canvas canvas) é um método público de View que chama onDraw e dispatchDraw. O método draw não foi projetado para ser sobrescrito — em vez disso, onDraw é sobrescrito. Após a execução de onDraw para a View atual, dispatchDraw é chamado, que invoca recursivamente draw para todas as Views filhas (se a View atual for um contêiner ViewGroup).
onDraw é chamado nas seguintes condições: 1) a View se torna visível pela primeira vez (após setVisibility(VISIBLE)); 2) o aplicativo chama invalidate() nesta View ou em seu pai; 3) o tamanho da View muda (novo layout); 4) uma animação associada à View muda (via ValueAnimator ou ObjectAnimator); 5) o estado da View muda (pressed, focused, enabled).
Desde Android 3.0 (API 11), a maioria das operações do Canvas são aceleradas por hardware através da GPU. O desenvolvedor pode verificar se a aceleração está habilitada através de canvas.isHardwareAccelerated(). Com a aceleração de hardware, algumas operações do Canvas não são suportadas (por exemplo, clipPath com uma região não retangular) e podem ser ignoradas ou causar erros.
Canvas fornece um rico conjunto de métodos de desenho. Todos os métodos funcionam no sistema de coordenadas atual, que pode ser modificado através de transformações (translate, rotate, scale, skew). Cada método aceita um objeto Paint que define cor, estilo, espessura de linha, efeitos e suavização.
| Método do Canvas | Propósito | Parâmetros chave do Paint |
|---|---|---|
| drawCircle | Desenhar um círculo | centerX, centerY, radius, paint |
| drawRect | Desenhar um retângulo | left, top, right, bottom, paint |
| drawLine | Desenhar uma linha | startX, startY, endX, endY, paint |
| drawPath | Desenhar um traçado arbitrário | path, paint |
| drawBitmap | Desenhar uma imagem bitmap | bitmap, srcRect, dstRect, paint |
| drawText | Desenhar texto | text, x, y, paint |
| drawArc | Desenhar um arco ou setor | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | Desenhar uma oval | ovalRect, paint |
Paint define o estilo de desenho: cor (setColor), espessura de linha (setStrokeWidth), estilo de preenchimento (setStyle — FILL, STROKE, FILL_AND_STROKE), suavização (setAntiAlias), efeitos (setShadowLayer, setMaskFilter) e gradientes (setShader). Recomenda-se criar e configurar Paint uma vez no construtor da View, e não dentro de onDraw, para evitar a criação de objetos no loop de desenho.
Canvas usa um sistema de coordenadas onde o eixo X aponta para a direita e o eixo Y para baixo. A origem (0, 0) é o canto superior esquerdo da View. Antes de chamar os métodos de desenho, o desenvolvedor pode alterar o sistema de coordenadas através de canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py). Todas as operações de desenho subsequentes serão realizadas no sistema transformado.
onDraw é executado na thread principal de UI, e sua duração afeta diretamente a taxa de quadros (fps). Para manter 60 fps, o método onDraw deve ser concluído em até 16 milissegundos. Qualquer lentidão leva a quedas de quadros (jank), que visualmente aparece como gaguejo de animação ou atraso na rolagem.
Regras principais para otimização de onDraw incluem: 1) não criar objetos dentro de onDraw — todos os objetos Paint, Path, Rect devem ser campos de classe; 2) não alocar memória (new) — isso aciona coleta de lixo (GC) e quedas de quadros; 3) armazenar em cache operações complexas em um Bitmap via Bitmap.createBitmap e desenhar o Bitmap preparado; 4) não realizar operações de E/S ou carregamento de recursos.
clipRect limita a área de desenho a um retângulo especificado. É usado para evitar que o Canvas perca tempo desenhando pixels fora da área visível. É especialmente eficaz ao rolar listas grandes ou aplicar zoom: defina clipRect para a área visível (dirty rect), e o Canvas desenhará apenas o que é visível.
canvas.save() salva o estado atual do Canvas (transformação e clip) em uma pilha. canvas.restore() restaura o último estado salvo. Isso é necessário para transformações temporárias: salve o estado, aplique translate/rotate, desenhe, depois restaure. save e restore são leves e não afetam o desempenho quando usados de forma razoável (menos de 10–15 níveis de aninhamento).
invalidate() é um método da View que informa ao sistema que o conteúdo da View está desatualizado e precisa ser redesenhado. Chamar invalidate inicia um novo ciclo de draw no qual onDraw será chamado. invalidate deve ser chamado apenas da thread de UI (main thread). Para chamar de uma thread em segundo plano, use postInvalidate.
postInvalidate() é uma versão thread-safe de invalidate que envia uma solicitação de redesenho para a fila da thread principal através de Handler. É usada em tarefas em segundo plano quando a View precisa ser atualizada após a conclusão do carregamento de dados ou cálculos. Pode ser combinada com runOnUiThread para sincronização com a UI.
Para redesenho parcial, use invalidate(Rect dirty) ou invalidate(int l, int t, int r, int b). Isso informa ao sistema que apenas a área especificada mudou, não a View inteira. O Android otimiza o desenho redesenhando apenas o dirty rect, melhorando significativamente o desempenho em animações com atualizações parciais.
Vamos ver exemplos práticos de sobrescrita de onDraw para criar Views personalizadas no Android usando Kotlin.
Este exemplo cria um indicador de progresso circular com preenchimento animado. onDraw desenha dois círculos: um círculo de fundo cinza e um círculo colorido cujo arco é determinado pelo progresso atual. Chamar invalidate em setProgress garante o redesenho quando o valor muda.
class CircularProgressView
@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private val bgPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.parseColor("#E0E0E0")
isAntiAlias = true
}
private val progressPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.BLUE
isAntiAlias = true
strokeCap = Paint.Cap.ROUND
}
var progress: Float = 0f
set(value) {
field = value.coerceIn(0f, 1f)
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val size = minOf(width, height) - 24f
val oval = RectF(12f, 12f,
12f + size, 12f + size)
canvas.drawArc(oval, 270f, 360f, false, bgPaint)
canvas.drawArc(oval, 270f,
progress * 360f,
false, progressPaint)
}
}
Este exemplo desenha um retângulo com gradiente e texto sobre ele. O gradiente é criado via LinearGradient (Shader), e o texto via drawText. Ambos os objetos Paint são criados uma vez no construtor, enquanto onDraw apenas atualiza o tamanho do retângulo com base na largura atual da View.
class GradientTextView(context: Context)
: View(context) {
private val shaderPaint = Paint().apply {
isAntiAlias = true
}
private val textPaint = Paint().apply {
color = Color.WHITE
textSize = 48f
isAntiAlias = true
textAlign = Paint.Align.CENTER
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val gradient = LinearGradient(
0f, 0f, width.toFloat(), 0f,
Color.parseColor("#FF6B35"),
Color.parseColor("#FF0040"),
Shader.TileMode.CLAMP
)
shaderPaint.shader = gradient
canvas.drawRect(0f, 0f,
width.toFloat(),
height.toFloat(), shaderPaint)
val xCenter = width / 2f
val yCenter = height / 2f
val yOffset = (textPaint.descent() +
textPaint.ascent()) / 2f
canvas.drawText("Hello from Canvas",
xCenter, yCenter - yOffset,
textPaint)
}
}
Este exemplo cria um relógio animado que atualiza o desenho a cada 1000 milissegundos. postDelayed dentro de onDraw chama invalidate após um segundo, criando um loop infinito de redesenho. Os ponteiros do relógio são desenhados via drawLine com diferentes comprimentos e espessuras.
class AnalogClockView(context: Context)
: View(context) {
private val handPaint = Paint().apply {
style = Paint.Style.STROKE
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = minOf(cx, cy) - 20f
val now = Calendar.getInstance()
val hours = now.get(Calendar.HOUR) % 12
val minutes = now.get(Calendar.MINUTE)
val seconds = now.get(Calendar.SECOND)
val secondAngle = Math.toRadians(
seconds * 6f - 90f)
handPaint.strokeWidth = 2f
handPaint.color = Color.RED
canvas.drawLine(cx, cy,
cx + cos(secondAngle) * radius * 0.8f,
cy + sin(secondAngle) * radius * 0.8f,
handPaint)
postDelayed({ invalidate() }, 1000)
}
}
Perguntas frequentes
Não, não se deve chamar onDraw diretamente. Para solicitar um redesenho, use invalidate() (da thread de UI) ou postInvalidate() (de uma thread em segundo plano). O sistema decidirá quando chamar onDraw e otimizará o desenho combinando múltiplas chamadas invalidate em uma única passagem.
Razões: a View está oculta (setVisibility(GONE) ou INVISIBLE); a View não foi adicionada à hierarquia da Window; o tamanho da View é 0 (width ou height = 0); setWillNotDraw(true) é chamado (verdadeiro por padrão para ViewGroup). Verifique cada um desses pontos ao depurar.
A 60 fps, onDraw pode ser chamado até 60 vezes por segundo. A 120 fps (em dispositivos com telas de 120 Hz) — até 120 vezes. Cada chamada deve ser concluída em aproximadamente 8–16 milissegundos. Durante animações ativas via ObjectAnimator ou ValueAnimator, a frequência é determinada pelo número de quadros da animação.
onDraw é responsável por desenhar o conteúdo da própria View. dispatchDraw é responsável por desenhar as Views filhas (em ViewGroup). dispatchDraw é chamado após onDraw e percorre recursivamente todos os elementos filhos. Para uma View comum (não ViewGroup), dispatchDraw é vazio.
Sim, Canvas pode ser criado para um Bitmap através de Canvas(bitmap) e desenhado em qualquer thread. Isso é usado para pré-renderizar gráficos complexos em segundo plano. No entanto, o Canvas passado para onDraw pertence ao sistema e só pode ser usado na thread de UI dentro do método onDraw.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também