0Pricing
Android Academy · Aula

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 (chama onDraw). Use quando o conteúdo mudar, mas o tamanho permanecer igual.
  • requestLayout() — mede novamente e redesenha (chama onMeasure e depois onDraw). 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 = 75f

Visualizaçõ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 View com @JvmOverloads constructor
  • onMeasure() — informe o tamanho desejado com setMeasuredDimension()
  • Paint — defina o estilo; crie uma vez, não dentro de onDraw()
  • onDraw(canvas) — desenhe círculos, arcos, texto e bitmaps
  • Use invalidate() para redesenhar e requestLayout() para medir novamente
  • Atributos personalizados por meio de attrs.xml para 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

  1. Fundamentos do Material Design
  2. Temas e estilos
  3. Views personalizadas
  4. Animações e transições
  5. Navegação inferior e abas
← Voltar para Android Academy