Interview QuestionPractical QuestionFollow-up Questions

Custom Views in Android Using XML

skydovesJaewoong Eum (skydoves)||8 min read

Custom Views in Android Using XML

Android's View system allows you to create custom UI components by extending base View classes and overriding their drawing, measurement, and layout methods. When combined with custom XML attributes, these components become reusable widgets that other developers can configure declaratively in layout files just like built in views. Understanding the custom view lifecycle, the attribute system, and the measurement contract is necessary for building reliable custom components. By the end of this lesson, you will be able to:

  • Explain the purpose of each View constructor overload and when Android calls them.
  • Override onDraw() to render custom graphics using Canvas.
  • Define custom attributes in attrs.xml and read them with TypedArray.
  • Override onMeasure() to control how your view reports its size.
  • Apply best practices for performance in custom view implementations.

View Constructors and Inflation

Android View classes require specific constructor overloads depending on how the view is created. When inflated from XML, the system calls the two parameter constructor with Context and AttributeSet. The three parameter constructor adds a default style attribute. Kotlin's @JvmOverloads annotation generates all three overloads from a single declaration:

class CircleView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr)

The AttributeSet parameter contains every XML attribute specified on the view tag. The defStyleAttr parameter points to a theme attribute that provides default values for the view's styled attributes. When you create a view programmatically with just a Context, attrs is null and no XML attributes are available.

Drawing with onDraw() and Canvas

The onDraw() method is where you render your view's content. Android calls it whenever the view needs to be redrawn, which happens after invalidate() is called or when the view first becomes visible. The method receives a Canvas object that provides drawing primitives:

private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    color = Color.RED
    style = Paint.Style.FILL
}

override fun onDraw(canvas: Canvas) {
    val cx = width / 2f
    val cy = height / 2f
    val radius = minOf(cx, cy) * 0.8f
    canvas.drawCircle(cx, cy, radius, paint)
}

Two performance rules apply inside onDraw(). First, never allocate objects. The method is called on every frame during animations, and allocations trigger garbage collection pauses that cause dropped frames. Create Paint, Rect, Path, and other drawing objects as class fields and reuse them. Second, minimize the drawing area. Use canvas.clipRect() to restrict drawing to the invalidated region when possible.

This interview continues for subscribers

Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.

Become a Sponsor