Graphics Using Jetpack Compose | Kodeco | Total Tech

Android has quite a lot of graphics-based objects to position on the app’s display screen. A few of these graphics objects embody textual content, buttons and checkboxes. What if you happen to wished to attract customized graphics? Like rectangles, traces, triangles and different shapes? Video video games, portray apps or chart drawing applications want customized graphics. Once you desire a good avatar to your subsequent recreation, customized graphics with Jetpack Compose would be the solution to create it.

You draw customized graphics on a particular view known as a Canvas with a Paint interface. When working with Jetpack Compose, you utilize the Graphics API. The Graphics API makes use of an strategy known as the declarative programming paradigm. On this tutorial, you’ll be taught this less complicated manner to make use of the Graphics API on Canvas.

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

  • Draw primitive shapes with customized graphics.
  • Create complicated customized graphics by combining graphics objects.
  • Show textual content utilizing Paint.
  • Rework objects.
Observe: This tutorial assumes you’ve expertise with Android and Kotlin. If that’s not the case, take a look at the Starting Android Improvement with Kotlin collection and different Kotlin and Android tutorials to get familiarized first.

Getting Began

Begin through the use of the Obtain Supplies button on the prime or backside of this tutorial to obtain the undertaking.

Open the undertaking in Android Studio Bumblebee or later and get conversant in the recordsdata. You’ll discover a starter undertaking and a last undertaking. Open and run the starter undertaking. The app – Pacman – comprises a clean, white display screen.

Humble beginnings to date, proper? If you happen to’re questioning in regards to the finish results of the undertaking, open and run the ultimate undertaking. You’ll see the Pacman display screen:

Full Pacman App custom graphics with Jetpack Compose

Hopefully, seeing the ultimate app will get you fired as much as begin drawing some superior Pacman graphics. So with that, waka, waka, chomp, chomp. Time to get to it!

Creating Jetpack Compose Customized Graphics

You draw graphics like buttons, textual content fields and pickers by inserting them on a view. “Graphics” on this tutorial refers to customized drawings like rectangles, triangles and contours. The graphics right here, known as primitives, aren’t like refined shapes like buttons. You can create graphics utilizing bitmap or SVG, however the Graphics API in Android offers you another solution to do it. As an alternative of drawing graphics utilizing instruments like Adobe Illustrator and importing them as bitmap or SVG, you possibly can create uncooked graphics instantly by way of code.

Utilizing solely code, you continue to create a bitmap with pixels and can see the identical pictures on the Android display screen. Your code makes use of Canvas to attract objects utilizing a bitmap. As an alternative of placing sure colours in particular x and y places, you utilize helper strategies to attract widespread shapes like traces, rectangles and circles. Lastly, to vary the bitmap’s fashion and colours, you utilize the Paint interface.

Utilizing Declarative Graphics API

The Graphics API has been within the Android SDK since its inception, API stage 1, in 2008. What’s new is a declarative manner to make use of this API: It makes managing Canvas and Paint a simple process. You don’t have to set methodology or different configurations on the Paint object. As an alternative, all of the configuration and execution occur in a single place: the composable operate. Earlier than Jetpack Compose, working with the Paint API required meticulous element since code group choices may trigger noticeable efficiency inefficiencies. Moreover, working with Canvas might be complicated. However with Compose, it’s a breeze to create graphics.

Understanding Canvas

What’s Canvas?

To attract an object on the Android display screen, first you want a Drawable to carry your drawing. Drawings are composed of pixels. If you happen to’ve labored in Android very lengthy, you’ll learn about Drawable. You utilize this class if you wish to show a picture. Canvas holds the strategies to attract shapes. So, with Drawable, you override the draw methodology. The draw methodology accepts Canvas because the argument. This connection lets you draw shapes utilizing code in Drawable to place them on the Canvas.

What are you able to do with Canvas?

Canvas lets you draw many primitive shapes, from circles to clipping the shapes. You can say that Canvas is an abstraction of Drawable. You load up a picture with Drawable by inflating SVG or PNG recordsdata. Inflating doesn’t want Canvas. However if you happen to wished to attract a primitive form dynamically, you’d use Canvas.

Consider Canvas as a layer on prime of Drawable the place you would draw quite a lot of shapes.

Making a Canvas

Making a Canvas is easy. Within the starter undertaking, open MainActivity.kt and take a look at CanvasApp:

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

Canvas accepts a modifier that permits you to modify the Canvas’s measurement, for instance. On this instance, you set the dimensions to the utmost measurement of the father or mother.

Once you ran the starter undertaking, you didn’t see something, but it surely’s time to vary that. Have a look at the primary two traces contained in the Canvas block:

    val canvasWidth = measurement.width
    val canvasHeight = measurement.peak

Contained in the Canvas block, you would question the dimensions of Canvas from measurement. Bear in mind the modifier above that extends to the utmost width and peak? That’s the dimensions of Canvas.

Drawing on a Canvas With Jetpack Compose

Following the primary two traces is a name to drawBlackScreen:

    drawBlackScreen(scope = this, canvasWidth, canvasHeight)

Go inside drawBlackScreen. It’s empty proper now. Put the next code inside it:

scope.drawRect(Coloration.Black,
    measurement=Measurement(canvasWidth, canvasHeight))

As its identify suggests, drawRect attracts a rectangle. It’s value noting that considerate methodology names are an amazing assist in code growth. You recognize what drawRect does by its identify: It attracts a rectangle. However what are its parameters? The primary is the colour of the rectangle and the second parameter is the dimensions of the rectangle.

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

Construct and run the app. You’ll see a black rectangle over the total display screen:

Black rectangle on Pacman app, custom graphics with Jetpack Compose

In your utilization, you omitted the Paint object argument, however you would provide one to vary the fashion of the drawn rectangle. You’ll see the best way to assemble Paint later. You additionally omitted the place argument. This implies you used the default worth for the place, which is 0 for x and y coordinates. Different parameters outline shade and measurement, that are widespread to all objects. Different object strategies require totally different parameters in accordance with the thing form. drawCircle wants an argument for radius, however the line object doesn’t.

Utilizing Modifier

You’ve seen Modifier within the Canvas operate. However you’ve different arsenals as properly. Suppose you wish to add some padding. Change the Modifier inside Canvas to:

  Canvas(modifier = Modifier
    .fillMaxHeight()
    .fillMaxWidth()
    .padding(50.dp)
  ) {

Don’t overlook to import padding. Every methodology on the modifier returns an up to date Modifier occasion. So by chaining the strategy calls, you’re steadily constructing the Canvas. The order by which you name the strategies matter.

Rebuild the undertaking and run it. You’ll see the black display screen now has white padding:
Black rectangle with white padding

Now that you just’ve seen how padding works for customized graphics in Jetpack Compose, you possibly can take away that code.

Creating Objects With Jetpack Compose

It’s lastly time to strive to attract some shapes! You’ll see that every of those shapes has its personal traits.

Drawing Strains

Now that you just’ve created a rectangle, it’s time to create different shapes. You’ll begin by drawing a part of the Pacman maze. Go inside drawBlueLines. You discover that there’s an annotation on prime of this methodology, @OptIn(ExperimentalGraphicsApi::class). It’s wanted since you use Coloration.hsv, which is experimental. So what is that this methodology? It will get a shade that you just’ll use to attract on Canvas. HSV (hue, saturation, worth) is likely one of the shade areas in addition to RGB (pink, inexperienced, blue). You possibly can learn extra about HSV in Picture Processing in iOS Half 1: Uncooked Bitmap Modification. Coloration.hsv accepts three arguments: hue, saturation and worth. The saturation and worth ranges from 0 to 1. It’s a float which represents the share worth.

On this methodology, you’ll want to draw 4 traces. You’ve already received the positions outlined for you.

Add the next code at // 2. Use the drawLine methodology:

    scope.drawLine(
      blue, // 1
      Offset(0f, line), // 2
      Offset(canvasWidth, line), // 2
      strokeWidth = 8.dp.worth // 3
    )

Right here’s what is occurring:

  1. blue is the colour you bought from Coloration.hsv.
  2. These outline the dot positions that make a line when related. Every dot wants an Offset. Principally, it’s an object that accepts two values, the x and y positions.
  3. This units the width of the stroke. The upper the worth, the thicker your line turns into. You outline a line by two factors. That’s why the strategy to attract a line wants two Offset arguments. It’s totally different from the strategy for drawing a rectangle.

Rebuild the undertaking and run the app. You’ll see 4 blue traces:

Four blue lines, custom graphics with Jetpack Compose

Discover that you just’ve drawn traces after drawing a rectangle — the order issues. If you happen to draw traces first, then draw a rectangle, the massive rectangle will cowl your traces.

Drawing Circles

Subsequent, you’ll draw an influence pellet. The circle represents an object that, if eaten by Pacman, makes him proof against ghosts for a sure time period. Go to // 3. Use the drawCircle methodology, and add the next:

  scope.drawCircle(purple, // 1
    heart = Offset(pacmanOffset.x + 600.dp.worth, dotYPos), // 2
    radius = radius) // 3

Right here’s what this code does:

  1. This specifies the colour of the circle.
  2. The heart argument refers back to the place of the middle of the circle in Canvas.
  3. The radius refers to how massive your circle is.

As you possibly can see, each strategies — whether or not drawing a rectangle, line or circle — settle for a shade argument. Nevertheless, they differ in different arguments as a result of each form is a bit totally different. All of the strategies settle for an non-obligatory Paint object.

Construct the undertaking and run the app. You’ll see a purple circle:

Purple circle, custom graphics with Jetpack Compose

With that, your energy pellet is able to give ol’ Blinky — spoiler — a run for his cash! :]

Drawing Level Strains

Within the Pacman video video games, this line of factors check with the dots that Pacman must eat to complete the sport. You possibly can create all of the factors one after the other, however you would additionally use a way to create a line consisting of factors. Discover // 4. Use the drawPoints methodology, and add the next:

  scope.drawPoints(factors, // 1
    PointMode.Factors, // 2
    purple, // 3
    strokeWidth = 16.dp.worth) // 4

This code defines:

  1. The checklist of Offsets the place you outlined the place of factors.
  2. The mode or fashion of the purpose. Right here, you render small squares. There are different PointMode choices. Strive them out earlier than shifting on. Press Ctrl (or CMD on a Mac) + House in your keyboard to see the opposite choices.
  3. Coloration.
  4. Line thickness.

Construct the undertaking and run the app. You’ll see a line of factors:

A Line of points, custom graphics with Jetpack Compose

Drawing Arcs

Now, right here comes probably the most thrilling a part of the tutorial: drawing Pacman himself! Pacman is a not-quite-full circle. You name this form a sector. You name 1 / 4 of a circle an arc. Pacman seems like a circle with an arc taken out!

Under // 5. Use the drawArc methodology inside drawPacman, add the next code:

  scope.drawArc(
    Coloration.Yellow, // 1
    45f, // 2
    270f, // 3
    true, // 4
    pacmanOffset,
    Measurement(200.dp.worth, 200.dp.worth)
  )

This code specifies:

  1. Yellow because the arc’s shade.
  2. Begin angle, which refers back to the backside a part of Pacman’s mouth.
  3. Sweep angle. Sum the beginning angle and the sweep angle, and also you’ll get the place of the highest a part of the mouth. Zero levels begins on the proper aspect of the circle. If you happen to consider the highest as north and backside as south, then zero levels is within the west path. You can change the beginning angle to zero and redraw Pacman to see the placement of zero levels.
  4. Whether or not you draw a line between the beginning angle and the tip angle utilizing the middle. If not, you draw a direct line between the beginning and finish angles. In your case, you wish to use the middle since you create a mouth by making a line from the beginning angle to the middle after which from the middle to the tip angle.

Construct the undertaking and run the app. You’ll see Pacman:

Pacman eating dots, custom graphics with Jetpack Compose

You possibly can see the distinction between utilizing the middle or not in drawing an arc within the image beneath:

Using the center in drawing an arc, custom graphics with Jetpack Compose

Drawing Complicated Shapes: Blinky the Ghost

The ghosts that chase your Pacman have a fancy form, and Jetpack Compose doesn’t have any “ghost” shapes in its customized graphics. :] So, you’ll draw a customized ghost form. To do that, you’ll want to divide a ghost into a number of easy shapes and draw them every utilizing the strategies you’ve realized.

You possibly can separate a ghost into totally different primitive shapes:

Ghost Dissected, custom graphics with jetpack compose

Drawing the Ghost’s Ft

Breaking down the ghost customized graphic, separate the ft. What do you see? Three arcs or half-circles lined up horizontally.

Go inside drawGhost and add the next code:

    val ghostXPos = canvasWidth / 4
    val ghostYPos = canvasHeight / 2
    val threeBumpsPath = Path().let 
      it.arcTo( // 1
        Rect(Offset(ghostXPos - 50.dp.worth, ghostYPos + 175.dp.worth),
          Measurement(50.dp.worth, 50.dp.worth)),
        startAngleDegrees = 0f,
        sweepAngleDegrees = 180f,
        forceMoveTo = true
      )
      it.arcTo( // 2
        Rect(Offset(ghostXPos - 100.dp.worth, ghostYPos + 175.dp.worth),
          Measurement(50.dp.worth, 50.dp.worth)),
        startAngleDegrees = 0f,
        sweepAngleDegrees = 180f,
        forceMoveTo = true
      )
      it.arcTo( // 3
        Rect(Offset(ghostXPos - 150.dp.worth, ghostYPos + 175.dp.worth),
          Measurement(50.dp.worth, 50.dp.worth)),
        startAngleDegrees = 0f,
        sweepAngleDegrees = 180f,
        forceMoveTo = true
      )
      it.shut()
      it
    
    scope.drawPath( // 4
      path = threeBumpsPath,
      Coloration.Crimson,
      fashion = Fill
    )

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

  1. arcTo is just like drawArc above: the startAngleDegrees and sweepAngleDegrees arguments are like begin and sweep angles the place the first argument is the rectangle that defines or bounds the dimensions of the arc. The final argument strikes the Path level to the tip of the trail earlier than drawing one other arc. In any other case, you’d at all times draw different arcs from the identical beginning place or starting of the primary arc.
  2. You probably did precisely the identical as above, solely you’re beginning on the finish of the primary one.
  3. For the final leg, you begin on the finish of the second leg.
  4. path argument is the trail you’ve created, and the second argument is the colour of your path. The third argument, fill, is whether or not you need to fill the trail with the chosen shade.

Rebuild the undertaking and run the app. You’ll see the ghost’s ft:

Ghost's feet, custom graphics with Jetpack Compose

Observe: As an alternative of utilizing drawPath, you would use drawArc thrice. Experiment with drawArc and see which one is extra handy for you.

Drawing the Ghost’s Physique

Now, you’ll draw a rectangle as the primary a part of the ghost’s physique. You already know the best way to construct a rectangle, so add the next code on the backside of drawGhost:

    scope.drawRect(
      Coloration.Crimson,
      Offset(ghostXPos - 150.dp.worth, ghostYPos + 120.dp.worth),
      Measurement(150.dp.worth, 82.dp.worth)
    )

Rebuild the undertaking and launch the app. You’ll see the ghost’s physique:

Ghost's body, custom graphics with Jetpack Compose

A ghost with a physique? Solely in Pacman. :]

Drawing the Ghost’s Head

The ghost’s head is a half-circle arc, however greater and in the other way of the ghost’s ft. Add the next code on the backside of drawGhost:

    scope.drawArc(
      Coloration.Crimson,
      startAngle = 180f,
      sweepAngle = 180f,
      useCenter = false,
      topLeft = Offset(ghostXPos - 150.dp.worth, ghostYPos + 50.dp.worth),
      measurement = Measurement(150.dp.worth, 150.dp.worth)
    )

Beginning on the prime left nook of the ghost’s physique, you draw an arc 180 levels to the best. Rebuild the undertaking and run the app. You’ll see the ghost’s head:

Ghost's full body, custom graphics with Jetpack Compose

Drawing the Ghost’s Eyes

Wow, all you’re lacking now are the eyes! The ghost has two eyes, with every eye composed of a white outer circle and a black internal circle for the iris. So now, you’ll draw 4 circles similar to you’ve already finished with the facility pellet. Add the next code on the backside of drawGhost:

    scope.drawCircle(
      Coloration.White,
      heart = Offset(ghostXPos - 100.dp.worth, ghostYPos + 100.dp.worth),
      radius = 20f
    )
    scope.drawCircle(
      Coloration.Black,
      heart = Offset(ghostXPos - 90.dp.worth, ghostYPos + 100.dp.worth),
      radius = 10f
    )

Rebuild the undertaking and run the app. You’ll see a one-eyed ghost:

One-eyed ghost, custom graphics with Jetpack Compose

Now, strive to attract the ghost’s left eye. Gotta have two eyes to catch Pacman. :]

Drawing Textual content With Jetpack Compose

To attract textual content, you’ll want to entry the native Canvas object as a result of you possibly can’t draw textual content on prime of Jetpack Compose’s Canvas. Inside drawScore, you’ll see that you’ve got textPaint:

  val textPaint = Paint().asFrameworkPaint().apply 
    isAntiAlias = true
    textSize = 80.sp.worth
    shade = android.graphics.Coloration.WHITE
    typeface = Typeface.create(Typeface.MONOSPACE, Typeface.BOLD)
    textAlign = android.graphics.Paint.Align.CENTER
  

Textual content is drawn as a customized graphic utilizing Jetpack Compose with Paint. You alter the color and style of the textual content by way of this interface. Usually with Canvas, you utilize the bottom Paint object, however since you’re utilizing the native Canvas object, you want the framework Paint methodology known as asFrameworkPaint. Contained in the asFrameworkPaint.apply block above, you configure the Paint object’s textual content for font, fashion, measurement and shade.

Moreover, there’s no drawText contained in the DrawingScope for the conventional Canvas object. It’s essential to name into the nativeCanvas interface to entry its drawText methodology. To attract textual content on the native Canvas, add the next code beneath // 7. Draw a textual content:

  scope.drawIntoCanvas 
    it.nativeCanvas.drawText( // 1
      "HIGH SCORE", // 2
      canvasWidth / 2, // 3
      canvasHeight / 3, // 3
      textPaint // 4
    )
    it.nativeCanvas.drawText( // 1
      "360", // 2
      canvasWidth / 2, // 3
      canvasHeight / 3 + 100.dp.worth, // 3
      textPaint // 4
    )
  

Right here’s what’s taking place:

  1. Just like the Path in arcTo earlier, it is the Canvas object contained in the drawIntoCanvas block. You reference the native Canvas object after which use drawText to attract the textual content.
  2. That is the textual content you wish to write.
  3. These are the x and y coordinates for textual content placement.
  4. That is the framework Paint object representing the textual content’s shade, measurement and font.

Construct the undertaking and run the app. You’ll see the next textual content on the display screen:

Score text, custom graphics with Jetpack Compose

Scaling, Translating and Rotating Objects With Jetpack Compose

You’ve finished quite a lot of drawing! Typically, after drawing objects, you would possibly want to remodel them. For instance, you would possibly wish to make them greater or change their place or path. You may additionally have to rotate Pacman as he strikes by way of the maze: When he strikes north, his mouth ought to level north.

Scaling and Translating Objects

Have a look at the ghost, and also you’ll see he’s smaller than Pacman. Additionally, the ghost’s place is barely decrease than Pacman’s. You possibly can repair this by reworking the ghost, which you’ll do now.

DrawingScope has the withTransform methodology. Inside this methodology, add the scale and translate modifiers. Inside drawGhost wrap each code there with the next snippet beneath:

  scope.withTransform( // 1
    scale(1.2f) // 2
    translate(prime=-50.dp.worth, left=50.dp.worth) // 3
  ) 
    ...
  

Right here’s what this code does:

  1. scope block makes use of the strategy withTransform. Contained in the block, withTransform makes use of two strategies, or modifiers, to remodel the thing.
  2. scale adjustments the dimensions of the thing. The argument 1.2f means the thing can be 20% greater.
  3. translate has two arguments, prime and left. prime adjustments the vertical place of the thing. The damaging worth, -50.dp.worth, means the thing rises upward. A constructive worth pushes the thing downward. The horizontal place of the thing adjustments with the left argument. The damaging worth, -50.dp.worth, means object strikes to the left. A constructive worth would transfer the thing to the best.

Construct the undertaking and run the app. You’ll see the ghost has moved barely up and develop into greater:

Bigger ghost, custom graphics with Jetpack Compose

Properly, take a look at that! A Blinky duplicate. Blinky could be proud. Waka do you suppose? :]

Rotating Objects

The Pacman recreation has a lives indicator, and including one will make this entire. The indicator is the Pacman form itself. So, if the indicator has three Pacman shapes, it means you’ve three lives. You already understand how to attract Pacman. Now, you’ll want to duplicate him. However that’s not sufficient. You additionally want to maneuver the clones, make them smaller, after which rotate them.

Go into drawPacmanLives, and add the next code:

  scope.withTransform(
    scale(0.4f)
    translate(prime=1200.dp.worth, left=-1050.dp.worth)
    rotate(180f)
  ) 
    drawArc(
      Coloration.Yellow,
      45f,
      270f,
      true,
      pacmanOffset,
      Measurement(200.dp.worth, 200.dp.worth)
    )
  

You’ve seen the drawArc code earlier than. It’s the code you used once you drew the unique Pacman. withTransform will scale, translate, and at last, rotate Pacman. rotate accepts the angle argument in levels.

Construct the undertaking and run the app. You’ll see one other Pacman however smaller and in a special place:

One Life of Pacman, custom graphics with Jetpack Compose

Now, strive to attract one other Pacman beside this clone to point that you’ve got two lives left within the recreation.

The place to Go From Right here?

Obtain the ultimate undertaking by clicking Obtain Supplies on the prime or backside of this tutorial.

You’ve realized in regards to the Graphics API in Jetpack Compose. You realized the best way to arrange Canvas and modify it utilizing a modifier. You’ve drawn many shapes and reworked them. You additionally used the native Canvas to attract textual content on it.

However there are extra belongings you haven’t explored, resembling animation. You can transfer Pacman and make his mouth open to simulate consuming the dots. In fact, you would give life to the ghost, make it flash blue, and have it chase Pacman.

Be at liberty to checkout this glorious Getting Began with Jetpack Compose Animations tutorial adopted by the extra superior Jetpack Compose Animations tutorial.

We hope you loved this tutorial! You probably have any feedback or questions, please be a part of the discussion board dialogue beneath.



Graphics Using Jetpack Compose | Kodeco

x