Canvas no Android — é uma classe do pacote android.graphics que fornece uma API para renderização 2D em View. Canvas funciona como uma tela na qual o desenvolvedor desenha formas geométricas, texto, imagens e trajetos usando vários métodos draw*. De acordo com a Documentação para Desenvolvedores Android (2025), o Canvas é a principal ferramenta para renderização personalizada no Android e é usado junto com a classe Paint para definir estilos e cores. Cada chamada onDraw() recebe uma instância Canvas pronta, associada ao buffer de bitmap da view.
Pontos Principais
Canvas — é a classe central de gráficos 2D no Android que fornece métodos para desenhar em um plano. Cada Canvas está vinculado a um Bitmap — uma área de memória onde os resultados das operações de desenho são registrados. Quando uma View é redesenhada, o sistema passa para onDraw() um Canvas já associado ao buffer dessa View.
O Canvas funciona em um sistema de coordenadas onde (0,0) é o canto superior esquerdo, o eixo X vai para a direita, o eixo Y para baixo. Todas as operações de desenho usam o estado atual do Canvas, incluindo a matriz de transformação, a área de recorte e o estilo Paint. De acordo com a Documentação Android (2025), a API Canvas inclui mais de 30 métodos draw* para diferentes tipos de primitivas gráficas.
Desde o Android 3.0 (API 11) o Canvas suporta aceleração por hardware através de DisplayList. No modo acelerado, as operações do Canvas são gravadas em uma DisplayList e depois executadas na GPU. Isso proporciona um ganho significativo de desempenho, mas impõe limitações: algumas operações do Canvas (clipPath com recorte não-Path) não são suportadas no modo acelerado.
O Canvas fornece um conjunto de métodos para desenhar primitivas básicas. Cada método aceita parâmetros geométricos e um objeto Paint que define o estilo visual.
| Método | Propósito | Exemplo |
|---|---|---|
| drawRect() | Desenha um retângulo | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | Desenha um círculo | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | Desenha uma linha | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | Desenha texto | canvas.drawText(“Hello”, 10f, 20f, paint) |
| drawBitmap() | Desenha uma imagem | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | Desenha um trajeto arbitrário | canvas.drawPath(path, paint) |
| drawOval() | Desenha uma oval | canvas.drawOval(rectF, paint) |
| drawArc() | Desenha um arco ou setor | canvas.drawArc(rectF, 0f, 90f, true, paint) |
Cada método pode ser chamado em qualquer ordem — o Canvas desenha na ordem das chamadas, sobrepondo novos elementos sobre os anteriores. Para composições complexas, é usada a técnica de renderização em múltiplas camadas com salvamento e restauração do estado do Canvas.
Paint — é a classe que define como o Canvas desenhará. Sem o Paint, os métodos do Canvas são inúteis — ele define cor, espessura da linha, estilo de preenchimento, tamanho do texto, efeitos (sombra, gradiente, desfoque) e modo de mesclagem.
O Paint possui propriedades-chave: color (cor), style (FILL, STROKE ou FILL_AND_STROKE), strokeWidth (espessura da linha), textSize (tamanho do texto), isAntiAlias (antialiasing), shader (gradiente ou imagem). Criar um objeto Paint é uma operação cara, portanto deve ser criado uma vez e reutilizado.
private val fillPaint = Paint().apply {
color = Color.parseColor("#FF6200EE")
style = Paint.Style.FILL
isAntiAlias = true
}
private val strokePaint = Paint().apply {
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 4f
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(100f, 100f, 80f, fillPaint)
canvas.drawCircle(100f, 100f, 80f, strokePaint)
}
Neste exemplo, dois objetos Paint são usados: um para preencher o círculo com cor roxa, o segundo para o traçado branco. Separar preenchimento e traçado é uma prática padrão que permite controle flexível da aparência sem recriar objetos.
Para preenchimentos gradientes, o Paint aceita um Shader — subclasses LinearGradient, RadialGradient, SweepGradient. O Shader é vinculado ao Paint através do método setShader() e define como a cor muda pela área de desenho.
O Canvas suporta transformações matriciais do sistema de coordenadas: translate() (translação), rotate() (rotação), scale() (escala) e skew() (inclinação). As transformações se acumulam: cada chamada subsequente é aplicada sobre as anteriores.
O gerenciamento da pilha de transformações é feito através de save() e restore(). save() salva o estado atual do Canvas (matriz, recorte) na pilha, restore() restaura o último estado salvo. Entre save() e restore(), transformações aninhadas podem ser aplicadas.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save() // Save initial state
canvas.translate(100f, 100f) // Translate origin
// First square
canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)
canvas.save() // Save after translate
canvas.rotate(45f, 25f, 25f) // Rotate around square center
// Rotated square
canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
canvas.restore() // Restore after rotate
canvas.restore() // Restore initial state
}
Sempre use save() antes de iniciar transformações e restore() depois de concluí-las. Pares save/restore desbalanceados levam ao acúmulo de matriz e renderização incorreta de elementos subsequentes.
Para gráficos complexos, o Canvas é combinado com Bitmap e Path. Bitmap é usado para carregar imagens e armazenar em cache quadros renderizados. Path é usado para construir curvas Bezier arbitrárias, polígonos e contornos complexos.
A técnica de renderização fora da tela: um Bitmap é criado, uma cena complexa é desenhada nele através de um Canvas separado, e então o Bitmap finalizado é exibido em onDraw() via drawBitmap(). Isso permite atualizar gráficos complexos sem redesenho completo a cada quadro.
private var offscreenBitmap: Bitmap? = null
private var offscreenCanvas: Canvas? = null
fun buildOffscreenScene(width: Int, height: Int) {
offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
offscreenCanvas = Canvas(offscreenBitmap)
// Render scene once
offscreenCanvas!!.drawColor(Color.WHITE)
drawGrid(offscreenCanvas!!)
drawComplexShape(offscreenCanvas!!)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
}
Path é usado para construir contornos complexos: curvas Bezier (quadTo, cubicTo), arcos (arcTo), linhas (lineTo). Path pode ser fechado (close()) para formas preenchidas ou aberto para linhas. O método principal drawPath() combina Path e Paint para a renderização final.
O desempenho da renderização do Canvas afeta diretamente os FPS do aplicativo. Os principais problemas: alocações excessivas em onDraw(), chamadas frequentes a invalidate() e uso subótimo da Aceleração por Hardware.
Paint, Path, Rect, RectF e outros objetos devem ser criados uma vez no inicializador ou método setup(). Cada alocação em onDraw() gera pressão no coletor de lixo, causando microlags. A exceção são objetos static ou companion object para objetos imutáveis.
Se a maior parte da View estiver oculta fora da área visível (por exemplo, em ScrollView), use canvas.clipRect() para limitar a renderização. A Aceleração por Hardware neste caso otimiza adicionalmente o recorte de pixels invisíveis.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Clip to visible area
canvas.save()
canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)
// Render large scene
renderScene(canvas)
canvas.restore()
}
Use View.setLayerType() com LAYER_TYPE_HARDWARE para Views com gráficos Canvas complexos que são atualizados raramente. Para animações atualizadas com frequência, use LAYER_TYPE_NONE para evitar atualizações caras da camada de hardware. Faça perfilamento usando GPU Profile Rendering e Android Studio Profiler.
Perguntas Frequentes
Sim, você pode criar Canvas(Bitmap) — um construtor que aceita um Bitmap. Isso é usado para renderização fora da tela: você desenha em um Bitmap e depois o exibe via drawBitmap(). O Canvas também pode ser criado a partir de Surface (para SurfaceView) ou através de Picture.
Canvas — é uma API para desenhar em View na thread da UI. SurfaceView — é um Surface separado que pode ser atualizado de uma thread em segundo plano através de seu próprio Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView é preferível para jogos e vídeo.
A razão mais comum são pares desbalanceados de save() e restore(). Se após uma transformação (translate, rotate) restore() não for chamado, o Canvas permanece em um sistema de coordenadas modificado, e os elementos subsequentes são desenhados com deslocamento. Verifique seus pares save()/restore().
Use Paint.setTextAlign(Paint.Align.CENTER) para centralização horizontal e Paint.getTextBounds() ou Paint.descent() / ascent() para vertical. Centralização: canvas.drawText(texto, centerX, centerY - (ascent + descent) / 2, paint).
O Canvas por si só não anima — ele apenas desenha o estado atual. A animação é alcançada através de chamadas sequenciais a invalidate() (ou postInvalidateOnAnimation()), desenhando um novo quadro em onDraw() a cada vez. Para animações suaves, use ValueAnimator ou Choreographer para sincronizar com Vsync.
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