Draw Rectangle and Oval Shapes on Android Canvas

Android Canvas provides drawing operations for shapes, text, paths, and bitmaps. A Canvas does not normally hold the drawing by itself; it directs drawing commands to a destination such as a bitmap or the surface supplied to a custom View’s onDraw() method.

In this tutorial, we shall draw rectangle and oval shapes on an Android screen using Kotlin. The original example uses ShapeDrawable to draw into a bitmap. Current alternatives using a custom View, Canvas.drawRect(), Canvas.drawOval(), and Canvas.drawRoundRect() are also included.

How Android Canvas, Paint, and Shape Bounds Work

  • Canvas: supplies methods that place pixels, shapes, text, and bitmaps on a drawing destination.
  • Paint: controls properties such as color, stroke width, fill or stroke style, anti-aliasing, and text size.
  • Bounds: define the area occupied by a drawable or shape.
  • Bitmap: stores pixels when an off-screen Canvas is constructed with a bitmap.
  • Custom View: receives a Canvas in onDraw() when Android asks the View to render itself.

The Canvas origin is at the upper-left corner. The x-coordinate increases toward the right, and the y-coordinate increases toward the bottom. Coordinates passed to drawing methods are measured in pixels unless the application converts another unit to pixels.

Steps to Draw ShapeDrawable Objects on Canvas

To draw a shape with ShapeDrawable, follow these steps:

  1. Create a ShapeDrawable with the required shape.
    • Use ShapeDrawable(OvalShape()) for an oval.
    • Use ShapeDrawable(RectShape()) for a rectangle.
  2. Set the drawing bounds with shapeDrawable.setBounds(left, top, right, bottom).
  3. Set the fill color through shapeDrawable.paint.color.
  4. Pass the Canvas to shapeDrawable.draw(canvas).

The bounds use four edges rather than an x-coordinate, y-coordinate, width, and height. Therefore, right must be greater than left, and bottom must be greater than top. An OvalShape fills the specified rectangular bounds; equal width and height produce a circle.

The following Android screen contains a rectangle and an oval drawn to a bitmap-backed Canvas.

Kotlin Android - Draw Shape (Rect, Oval) to Canvas - Example

Note : If you would like to dynamically draw onto Canvas like in 2D Games, you may create a thread that redraws onto canvas at a frequency set by FPS parameter and create the illusion of object movement. Find an example for the same at Android Game Example.

For a custom View, request another frame with invalidate() or postInvalidateOnAnimation() instead of calling onDraw() directly. Any separate rendering thread must use a drawing surface and synchronization model designed for that purpose.

Kotlin Example Using ShapeDrawable for a Rectangle and Oval

This example creates a 700 by 1000 pixel bitmap and supplies it to a Canvas. It draws a rectangle and an oval into the bitmap, then places that bitmap in the background of an ImageView.

The original code uses the Android Support Library and Kotlin Android synthetic view access because it was written for an older project. The code is retained unchanged. New Android projects normally use AndroidX and View Binding or findViewById().

ImageView Layout for the Canvas Bitmap

activity_main.xml

</>
Copy
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageV"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</android.support.constraint.ConstraintLayout>

MainActivity Code for the Bitmap-Backed Canvas

MainActivity.kt

</>
Copy
package com.tutorialkart.drawshapeoncanvas

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.RectShape
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.shapes.OvalShape


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val bitmap: Bitmap = Bitmap.createBitmap(700, 1000, Bitmap.Config.ARGB_8888)
        val canvas: Canvas = Canvas(bitmap)

        var shapeDrawable: ShapeDrawable

        // rectangle positions
        var left = 100
        var top = 100
        var right = 600
        var bottom = 400

        // draw rectangle shape to canvas
        shapeDrawable = ShapeDrawable(RectShape())
        shapeDrawable.setBounds( left, top, right, bottom)
        shapeDrawable.getPaint().setColor(Color.parseColor("#009944"))
        shapeDrawable.draw(canvas)

        // oval positions
        left = 100
        top = 500
        right = 600
        bottom = 800

        // draw oval shape to canvas
        shapeDrawable = ShapeDrawable(OvalShape())
        shapeDrawable.setBounds( left, top, right, bottom)
        shapeDrawable.getPaint().setColor(Color.parseColor("#009191"))
        shapeDrawable.draw(canvas)

        // now bitmap holds the updated pixels

        // set bitmap as background to ImageView
        imageV.background = BitmapDrawable(getResources(), bitmap)
    }
}

The first drawable fills the bounds from (100, 100) to (600, 400). The second fills the bounds from (100, 500) to (600, 800). Because both bounds are wider than they are tall, the second shape is an oval rather than a circle.

Android Canvas Example Project Structure

The project places the activity source under the application’s Java or Kotlin package and the layout under res/layout.

Kotlin Android - Draw Shape (Rect, Oval) to Canvas - Example

Draw Rectangle and Oval Directly in a Custom Android View

A custom View is suitable when the shapes belong to a reusable UI component and must respond to the View’s measured size. Android passes a Canvas to the View’s onDraw() method whenever the View needs to render.

The following View draws a green rectangle in the upper portion of its available area and a teal oval below it. It creates the Paint objects once instead of allocating them during every draw pass.

</>
Copy
package com.tutorialkart.drawshapeoncanvas

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.View

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

    private val rectanglePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#009944")
        style = Paint.Style.FILL
    }

    private val ovalPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#009191")
        style = Paint.Style.FILL
    }

    private val rectangleBounds = RectF()
    private val ovalBounds = RectF()

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val horizontalInset = width * 0.15f

        rectangleBounds.set(
            horizontalInset,
            height * 0.10f,
            width - horizontalInset,
            height * 0.40f
        )

        ovalBounds.set(
            horizontalInset,
            height * 0.55f,
            width - horizontalInset,
            height * 0.85f
        )

        canvas.drawRect(rectangleBounds, rectanglePaint)
        canvas.drawOval(ovalBounds, ovalPaint)
    }
}

Add the custom View to an XML layout using its complete package name:

</>
Copy
<com.tutorialkart.drawshapeoncanvas.ShapeView
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Using the measured width and height makes the drawing adapt to the actual View size. If the bounds depend on size but are expensive to calculate, update them in onSizeChanged() and reuse them in onDraw().

What the Android onDraw Method Does

onDraw(canvas: Canvas) is the custom View callback used to render the View’s visual content. Android invokes it as part of the View drawing process. The supplied Canvas is already associated with the correct drawing destination and coordinate system for that View.

  • Override onDraw() in a custom View when the View needs custom graphics.
  • Call super.onDraw(canvas) when the superclass may have content to draw.
  • Do not call onDraw() yourself; call invalidate() when a state change requires redrawing.
  • Avoid creating large objects or performing blocking work inside onDraw().
  • Prepare reusable Paint, Path, Rect, and RectF objects outside the method where practical.

The Android documentation on custom View drawing explains how measurement and drawing callbacks work together.

Draw a Rounded Rectangle on Android Canvas

Use Canvas.drawRoundRect() when the rectangle needs rounded corners. The horizontal and vertical radius values control the curvature of each corner.

</>
Copy
private val roundedRectanglePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    color = Color.parseColor("#5E35B1")
    style = Paint.Style.FILL
}

private val roundedRectangleBounds = RectF(
    80f,
    120f,
    620f,
    420f
)

// Use the same value for rx and ry for evenly rounded corners.
canvas.drawRoundRect(
    roundedRectangleBounds,
    32f,
    32f,
    roundedRectanglePaint
)

The radius values are pixels in this example. For a consistent visual size across screen densities, convert a density-independent value to pixels before drawing.

Convert dp Values to Canvas Pixels

Canvas coordinates use pixels, while Android layouts commonly use density-independent pixels. Convert a dp measurement through display density when a shape, corner radius, margin, or stroke should have a comparable physical size across devices.

</>
Copy
private fun Float.dpToPx(): Float {
    return this * resources.displayMetrics.density
}

val cornerRadius = 16f.dpToPx()
val strokeWidth = 2f.dpToPx()

When a drawing is proportionally sized to its View, calculations based on width and height may be more appropriate than fixed dp dimensions.

Draw Filled and Outlined Android Canvas Shapes

The Paint style determines whether a shape is filled, outlined, or both. An outline is drawn inward and outward around the specified path or boundary, so allow enough space near the View edges for the complete stroke.

</>
Copy
val fillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    color = Color.CYAN
    style = Paint.Style.FILL
}

val outlinePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    color = Color.DKGRAY
    style = Paint.Style.STROKE
    strokeWidth = 4f
}

canvas.drawOval(ovalBounds, fillPaint)
canvas.drawOval(ovalBounds, outlinePaint)

Draw the fill first and the outline second so the border remains visible over the filled shape.

Draw Rectangle and Oval with Jetpack Compose Canvas

Jetpack Compose provides a separate Canvas composable with a declarative drawing API. The following example draws shapes relative to the composable’s available size:

</>
Copy
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color

@Composable
fun ShapeCanvas() {
    Canvas(modifier = Modifier.fillMaxSize()) {
        val horizontalInset = size.width * 0.15f
        val shapeWidth = size.width - (horizontalInset * 2)

        drawRoundRect(
            color = Color(0xFF009944),
            topLeft = Offset(horizontalInset, size.height * 0.10f),
            size = Size(shapeWidth, size.height * 0.30f),
            cornerRadius = CornerRadius(24f, 24f)
        )

        drawOval(
            color = Color(0xFF009191),
            topLeft = Offset(horizontalInset, size.height * 0.55f),
            size = Size(shapeWidth, size.height * 0.30f)
        )
    }
}

The View Canvas and Compose Canvas APIs serve similar drawing needs but use different types and lifecycles. Use the implementation that matches the application’s UI toolkit.

Android Canvas Shape Drawing FAQs

What is the onDraw method in Android?

onDraw() is a custom View callback that receives the Canvas used to render that View. Override it to draw custom content, and call invalidate() when a state change requires another draw pass.

How do I draw a rectangle on an Android Canvas?

Create a Paint object, define the rectangle’s edges with coordinates or a RectF, and call canvas.drawRect(). A ShapeDrawable(RectShape()) can also draw a rectangle after its bounds and paint properties are configured.

How do I draw an oval or circle on Android Canvas?

Call canvas.drawOval() with rectangular bounds. If the bounds have equal width and height, the result is a circle. You can also use OvalShape inside a ShapeDrawable.

How do I draw a rectangle with rounded corners?

Call canvas.drawRoundRect() and supply the rectangle bounds, horizontal radius, vertical radius, and Paint. Equal radius values create evenly rounded corners.

Why is my Android Canvas shape clipped or not visible?

Check that the right edge exceeds the left edge, the bottom exceeds the top, the bounds fall inside the View or bitmap, and the Paint has a visible color and suitable style. Also check for Canvas translations, clipping regions, transparent colors, and strokes positioned partly outside the available area.

Android Canvas Shape Drawing QA Checklist

  • Verify that rectangle and oval bounds remain inside the Canvas or custom View.
  • Confirm that right > left and bottom > top for every shape.
  • Test the drawing on screens with different sizes, densities, and orientations.
  • Check fill, stroke, stroke width, color contrast, and anti-aliasing settings.
  • Confirm that large Paint, Path, Rect, Bitmap, or shader objects are not repeatedly allocated inside onDraw().
  • Call invalidate() only when drawing state changes and do not invoke onDraw() directly.
  • Check that bitmap dimensions and configuration do not consume unnecessary memory.
  • Verify that the implementation uses either View Canvas types or Compose Canvas types consistently.

Kotlin Android Canvas Rectangle and Oval Summary

In this Kotlin Android TutorialDraw Shapes to Canvas, we have learnt to draw rectangle and oval shapes using ShapeDrawable, direct Canvas drawing methods, a custom View, and Jetpack Compose Canvas. Shape bounds determine position and size, while Paint controls color, fill, outline, stroke width, and rendering quality.