How to implement View.onDraw() method?

The onDraw(canvas: Canvas) method is often necessary to override when creating a custom view.
Canvas and Paint are two primary objects used in the implementation of the onDraw() method.

The Canvas is passed as a parameter to the onDraw() and is responsible for what gets drawn. It defines the shapes that are drawn within the onDraw() through methods like draw...(). Most draw...() methods take a Paint object as a parameter.

Paint dictates how things are drawn. It defines the color, font, style, etc., that are used when drawing a shape specified by the Canvas.

The onDraw() method is frequently called, and the speed at which this method executes affects how quickly the user interface renders. An essential rule in implementing onDraw() is to avoid creating new objects within this method. This means all Paint objects used in onDraw() should be created before the method is called, such as when the view is created.
Proper rendering of a view requires considering its size. Sometimes, it is necessary to perform complex calculations of dimensions and positions based on the size of the view. As processing speed is crucial for the onDraw(), it is considered good practice not to perform calculations within this method.

To handle changes in view size, the onSizeChanged() method is overridden. This method is called when the view's size is initially set and each time the size changes, which occurs far less frequently than calls to the onDraw() method. The parameters of the onSizeChanged() method are four integers that represent the new and old values of width and height. The initial call has the old values set to zero.
The dimensions passed to onSizeChanged() include padding. This means that to calculate the actual size of the view's content, these paddings need to be subtracted. Padding values are retrieved using methods like getPadding...().

All size calculations should be performed in onSizeChanged() and then used in onDraw().
After defining the dimensions and positions of the view components, you proceed to drawing. Each view has a differently implemented onDraw() method, but there are common groups of operations on Canvas and Paint used in drawing:
Discussions on implementing the onDraw() method often touch on optimizations for this method and other UI-related topics.

Avoid performing calculations and creating objects in the onDraw(). Creating new objects can trigger garbage collection, leading to Stop the World pauses and user interface lag. Create Paint objects during view initialization and reuse them in the onDraw(). Also, avoid creating objects during animations.

Another way to optimize the UI is to reduce the number of onDraw() calls. Most of them are caused by the execution of invalidate(), so try to avoid frequently calling this method.

Another costly operation is traversing the view hierarchy. During the execution of requestLayout(), the system goes through the entire hierarchy to determine the dimensions of each view. For some layouts, this traversal is performed multiple times, which can cause performance issues.
To create a smooth UI, it's necessary to reduce view nesting as much as possible. Try to create custom flat views instead of using several nested standard layouts. For non-universal views, it's easier to calculate dimensions based on their specific use in the application.