Writing a custom View

Views, layouts and RecyclerView Mid-level Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Spot the bug

This view stutters while animating. Which line is the problem?

  1. class Ring(context: Context) : View(context) {
  2. var progress = 0f
  3. override fun onDraw(canvas: Canvas) {
  4. val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { strokeWidth = 12f; style = Paint.Style.STROKE }
  5. canvas.drawArc(12f, 12f, width - 12f, height - 12f, -90f, progress * 360f, false, paint)
  6. }
  7. }
Hint 1 of 1

What runs on every frame?

Choose an answer, then open Show answer below to check it.

Show answer
  • Answer: line 4 val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { strokeWidth = 12f; style = Paint.Style.STROKE }

A new Paint is allocated on every frame. Create it once as a field and reuse it.

30-second answer

Extend View with the @JvmOverloads three-argument constructor, read custom attributes with context.withStyledAttributes, override onMeasure to honour the MeasureSpec, and draw in onDraw with Paint objects created once. Call invalidate() when appearance changes and requestLayout() only when size changes. Add accessibility info for anything interactive.

Practise saying it · 35 s. No recording, just a timer

Explain how to write a good custom View.

Say your answer out loud, then check which of these points you covered:

  • @JvmOverloads constructor and attributes
  • Honour MeasureSpec
  • No allocation in onDraw
  • invalidate vs requestLayout
  • Accessibility

Deep dive

Create Paint, Path and RectF objects as fields, never in onDraw.

Use resolveSize in onMeasure to respect the parent's constraints.

Save and restore state with onSaveInstanceState if the view holds user state.

In Compose, the equivalent is a Canvas or Modifier.drawBehind.

Common mistakes

  • Allocating in onDraw.
  • Ignoring MeasureSpec and always returning a fixed size.
  • Skipping contentDescription and focus support.

Senior take & follow-ups

Mention allocation-free drawing and accessibility; both are signs of production experience.

Follow-up questions

Think of your answer first, then open the hint.

  1. How do you support wrap_content?

    Show hint

    Compute a desired size and call resolveSize with the MeasureSpec.

  2. What's the Compose equivalent?

    Show hint

    Canvas or drawBehind, plus semantics for accessibility.

Suggest a fix if something here is wrong or out of date.