Graphics Using Jetpack Compose

Learn to create custom graphics using Jetpack Compose in Android with the convenient Canvas composable and the Paint object. By arjuna sky kok.

Leave a rating/review
Download materials
Save for later
Share

Android has a variety of graphics-based objects to place on the app’s screen. Some of these graphics objects include text, buttons and checkboxes. What if you wanted to draw custom graphics? Like rectangles, lines, triangles and other shapes? Video games, painting apps or chart drawing programs need custom graphics. When you want a nice avatar for your next game, custom graphics with Jetpack Compose will be the way to create it.

You draw custom graphics on a special view called a Canvas with a Paint interface. When working with Jetpack Compose, you use the Graphics API. The Graphics API uses an approach called the declarative programming paradigm. In this tutorial, you’ll learn this simpler way to use the Graphics API on Canvas.

In this tutorial, you’ll use Jetpack Compose to:

  • Draw primitive shapes with custom graphics.
  • Create complex custom graphics by combining graphics objects.
  • Display text using Paint.
  • Transform objects.
Note: This tutorial assumes you have experience with Android and Kotlin. If that’s not the case, check out the Beginning Android Development with Kotlin series and other Kotlin and Android tutorials to get familiarized first.

Getting Started

Start by using the Download Materials button at the top or bottom of this tutorial to download the project.

Open the project in Android Studio Bumblebee or later and get familiar with the files. You’ll notice a starter project and a final project. Open and run the starter project. The app – Pacman – contains a blank, white screen.

Humble beginnings so far, right? If you’re wondering about the end result of the project, open and run the final project. You’ll see the Pacman screen:

Full Pacman App custom graphics with Jetpack Compose

Hopefully, seeing the final app will get you fired up to start drawing some awesome Pacman graphics. So with that, waka, waka, chomp, chomp. Time to get to it!

Creating Jetpack Compose Custom Graphics

You draw graphics like buttons, text fields and pickers by placing them on a view. “Graphics” in this tutorial refers to custom drawings like rectangles, triangles and lines. The graphics here, called primitives, aren’t like sophisticated shapes like buttons. You could create graphics using bitmap or SVG, but the Graphics API in Android gives you an alternative way to do it. Instead of drawing graphics using tools like Adobe Illustrator and importing them as bitmap or SVG, you can create raw graphics directly through code.

Using only code, you still create a bitmap with pixels and will see the same images on the Android screen. Your code uses Canvas to draw objects using a bitmap. Instead of putting certain colors in specific x and y locations, you use helper methods to draw common shapes like lines, rectangles and circles. Finally, to change the bitmap’s style and colors, you use the Paint interface.

Using Declarative Graphics API

The Graphics API has been in the Android SDK since its inception, API level 1, in 2008. What’s new is a declarative way to use this API: It makes managing Canvas and Paint an easy task. You don’t need to set method or other configurations on the Paint object. Instead, all the configuration and execution happen in one place: the composable function. Before Jetpack Compose, working with the Paint API required meticulous detail since code organization decisions could cause noticeable performance inefficiencies. Additionally, working with Canvas can be confusing. But with Compose, it’s a breeze to create graphics.

Understanding Canvas

What is Canvas?

To draw an object on the Android screen, first you need a Drawable to hold your drawing. Drawings are composed of pixels. If you’ve worked in Android very long, you’ll know about Drawable. You use this class if you want to display an image. Canvas holds the methods to draw shapes. So, with Drawable, you override the draw method. The draw method accepts Canvas as the argument. This connection allows you to draw shapes using code in Drawable to put them on the Canvas.

What can you do with Canvas?

Canvas allows you to draw many primitive shapes, from circles to clipping the shapes. You could say that Canvas is an abstraction of Drawable. You load up an image with Drawable by inflating SVG or PNG files. Inflating doesn’t need Canvas. But if you wanted to draw a primitive shape dynamically, you’d use Canvas.

Think of Canvas as a layer on top of Drawable where you could draw a variety of shapes.

Creating a Canvas

Creating a Canvas is straightforward. In the starter project, open MainActivity.kt and check out CanvasApp:

  Canvas(modifier = Modifier
    .fillMaxHeight()
    .fillMaxWidth()
  ) {
...
  }

Canvas accepts a modifier that lets you modify the Canvas’s size, for example. In this example, you set the size to the maximum size of the parent.

When you ran the starter project, you didn’t see anything, but it’s time to change that. Look at the first two lines inside the Canvas block:

    val canvasWidth = size.width
    val canvasHeight = size.height

Inside the Canvas block, you could query the size of Canvas from size. Remember the modifier above that extends to the maximum width and height? That’s the size of Canvas.

Drawing on a Canvas With Jetpack Compose

Following the first two lines is a call to drawBlackScreen:

    drawBlackScreen(scope = this, canvasWidth, canvasHeight)

Go inside drawBlackScreen. It’s empty right now. Put the following code inside it:

scope.drawRect(Color.Black,
    size=Size(canvasWidth, canvasHeight))

As its name suggests, drawRect draws a rectangle. It’s worth noting that thoughtful method names are a tremendous help in code development. You know what drawRect does by its name: It draws a rectangle. But what are its parameters? The first is the color of the rectangle and the second parameter is the size of the rectangle.

By calling Size, Android Studio assists you in adding the required import, androidx.compose.ui.geometry.Size.

Build and run the app. You’ll see a black rectangle over the full screen:

Black rectangle on Pacman app, custom graphics with Jetpack Compose

In your usage, you omitted the Paint object argument, but you could supply one to change the style of the drawn rectangle. You’ll see how to construct Paint later. You also omitted the position argument. This means you used the default value for the position, which is 0 for x and y coordinates. Other parameters define color and size, which are common to all objects. Other object methods require different parameters according to the object shape. drawCircle needs an argument for radius, but the line object doesn’t.