Views personalizadas
Desenhe componentes personalizados da interface do zero. Estenda View, implemente onMeasure e onDraw com Canvas e Paint, defina atributos XML personalizados e crie views compostas.
Views personalizadas é uma aula grátis de Android Academy no CoddyKit. Esta é a aula 3 de 5. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Android Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Android Academy inclui 5 aulas no total.
Por que usar visualizações personalizadas?
Às vezes, as visualizações integradas do Android não fazem exatamente o que você precisa. As visualizações personalizadas permitem:
- Desenhar formas, gráficos, anéis de progresso ou diagramas que não existem no SDK
- Criar componentes de interface com a identidade da marca e usá-los de forma consistente em todo o app
- Combinar várias visualizações em um único componente reutilizável (visualização composta)
- Obter desempenho melhor eliminando hierarquias de layout aninhadas
Estendendo View
Crie uma visualização personalizada estendendo View (ou uma subclasse como ImageView). Substitua o construtor necessário:
class CircleProgressView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
var progress: Float = 0f
set(value) {
field = value.coerceIn(0f, 100f)
invalidate() // trigger redraw
}
// onMeasure and onDraw go here
}onMeasure()
onMeasure() informa ao pai qual tamanho esta visualização deseja ter. Sempre chame setMeasuredDimension() ao final:
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val desiredSize = 200 // dp → pixels
val px = (desiredSize * resources.displayMetrics.density).toInt()
val width = resolveSize(px, widthMeasureSpec)
val height = resolveSize(px, heightMeasureSpec)
// Square: same width and height
val size = minOf(width, height)
setMeasuredDimension(size, size)
}Paint — a ferramenta de desenho
Um objeto Paint armazena as informações de estilo do desenho (cor, largura do contorno e antialiasing). Crie-o uma vez — nunca dentro de onDraw():
class CircleProgressView ... : View(...) {
// Create Paint in init, not in onDraw!
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.LTGRAY
style = Paint.Style.STROKE
strokeWidth = 20f
strokeCap = Paint.Cap.ROUND
}
private val progressPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#6750A4")
style = Paint.Style.STROKE
strokeWidth = 20f
strokeCap = Paint.Cap.ROUND
}
}onDraw() — desenhando a visualização
onDraw(canvas) é chamado sempre que a visualização precisa ser redesenhada. Use o Canvas para desenhar formas, texto e bitmaps:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = (minOf(width, height) / 2f) - 30f
// Draw background circle
canvas.drawCircle(cx, cy, radius, backgroundPaint)
// Draw progress arc
val oval = RectF(cx - radius, cy - radius, cx + radius, cy + radius)
val sweepAngle = 360f * (progress / 100f)
canvas.drawArc(oval, -90f, sweepAngle, false, progressPaint)
}invalidate() e requestLayout()
Dois métodos para disparar uma atualização:
invalidate()— redesenha a visualização (chamaonDraw). Use quando o conteúdo mudar, mas o tamanho permanecer igual.requestLayout()— mede novamente e redesenha (chamaonMeasuree depoisonDraw). Use quando o tamanho da visualização mudar.
Atributos personalizados (attrs.xml)
Declare atributos XML personalizados para que a visualização possa ser configurada a partir dos arquivos de layout:
<!-- res/values/attrs.xml -->
<resources>
<declare-styleable name="CircleProgressView">
<attr name="progressColor" format="color" />
<attr name="trackColor" format="color" />
<attr name="strokeWidth" format="dimension" />
<attr name="progress" format="float" />
</declare-styleable>
</resources>Lendo atributos personalizados
Leia os atributos no construtor usando obtainStyledAttributes:
init {
context.obtainStyledAttributes(attrs, R.styleable.CircleProgressView).use { ta ->
progressPaint.color = ta.getColor(
R.styleable.CircleProgressView_progressColor,
Color.parseColor("#6750A4")
)
backgroundPaint.color = ta.getColor(
R.styleable.CircleProgressView_trackColor,
Color.LTGRAY
)
val sw = ta.getDimension(R.styleable.CircleProgressView_strokeWidth, 20f)
progressPaint.strokeWidth = sw
backgroundPaint.strokeWidth = sw
progress = ta.getFloat(R.styleable.CircleProgressView_progress, 0f)
}
}Usando a visualização personalizada em XML
Use sua visualização personalizada no XML do layout com o caminho completo da classe e os atributos personalizados:
<!-- In any layout XML -->
<com.example.myapp.CircleProgressView
android:id="@+id/progressView"
android:layout_width="120dp"
android:layout_height="120dp"
app:progress="65"
app:progressColor="@color/primary"
app:trackColor="@color/surface_variant"
app:strokeWidth="12dp" />
<!-- Control in code: -->
binding.progressView.progress = 75fVisualizações compostas
Uma visualização composta combina visualizações existentes em um único componente reutilizável. Estenda uma classe de layout como LinearLayout:
class SearchBarView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {
private val binding = ViewSearchBarBinding.inflate(LayoutInflater.from(context), this)
init {
orientation = HORIZONTAL
binding.ivSearch.setOnClickListener {
onSearchClick?.invoke(binding.etSearch.text.toString())
}
}
var onSearchClick: ((String) -> Unit)? = null
fun setHint(hint: String) { binding.etSearch.hint = hint }
}Desenhando texto
Desenhe texto em um Canvas com drawText(). Centralize-o com precisão usando Paint.getFontMetrics():
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.BLACK
textSize = 48f
textAlign = Paint.Align.CENTER
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
// Vertically center the text:
val fm = textPaint.fontMetrics
val textY = cy - (fm.ascent + fm.descent) / 2f
canvas.drawText("${progress.toInt()}%", cx, textY, textPaint)
}Verificação rápida
Qual método você deve chamar para disparar o redesenho da sua visualização personalizada quando os dados mudarem, mas o tamanho permanecer igual?
Recapitulação: visualizações personalizadas
Agora você pode desenhar qualquer coisa na tela:
- Estenda
Viewcom@JvmOverloads constructor onMeasure()— informe o tamanho desejado comsetMeasuredDimension()Paint— defina o estilo; crie uma vez, não dentro deonDraw()onDraw(canvas)— desenhe círculos, arcos, texto e bitmaps- Use
invalidate()para redesenhar erequestLayout()para medir novamente - Atributos personalizados por meio de
attrs.xmlpara configuração XML - Visualização composta — estenda um layout para combinar visualizações existentes
A seguir: dê vida à sua interface com animações e transições.
Perguntas Frequentes
A aula “Views personalizadas” é grátis?
Sim — o texto completo de “Views personalizadas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Android Academy, atualize para CoddyKit PRO. O curso de Android Academy inclui 5 aulas no total.
O que vou aprender em “Views personalizadas”?
Desenhe componentes personalizados da interface do zero. Estenda View, implemente onMeasure e onDraw com Canvas e Paint, defina atributos XML personalizados e crie views compostas. Você pratica Android Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Android Academy?
Nenhuma experiência prévia é necessária. Android Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 5.
Quanto tempo leva a aula “Views personalizadas”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Android Academy?
Sim. Cada aula de Android Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Fundamentos do Material Design
- Temas e estilos
- Views personalizadas
- Animações e transições
- Navegação inferior e abas