Javascript required
Skip to content Skip to sidebar Skip to footer

Drawing a Fill Circle in Android

Did you ever want to create highly-customized user interfaces in Android? So this is the tutorial for you!

To draw custom shapes, y'all need to go on iterating until you achieve the beautiful art you desire. In this tutorial, you'll learn how to draw your pattern on newspaper kickoff to avoid wasting time via trial and error.

You'll improve an app called Stars of Science. You'll larn how to create custom shapes by painting a profile menu with a curved custom shape and gradient colors.

Throughout the tutorial, you'll learn how to:

  • Prepare a custom shape on newspaper before coding.
  • Extend the Android View to draw and paint information technology on the Canvas.
  • Draw a curved shape in gradient colors.

The custom shape you'll create will look like this:

Android custom shape

Notation: This tutorial assumes you understand the basics of Android evolution with Kotlin. If you're new to Android development, please go through Beginning Android Development with Kotlin to understand the nuts. If you're new to Kotlin, check out this Introduction to Kotlin tutorial.

Getting Started

Download the project materials by clicking the Download Materials button at the summit or bottom of this tutorial. Launch Android Studio 3.half-dozen.1 or later on and select Open an existing Android Studio project. So navigate to and select the starter project binder where you'll observe the files you need to start, along with some widgets.

Your app already has its basic UI set up up and then you lot can focus on drawing custom shapes in Android.

Build and run the app. Yous'll meet the following screen on your mobile phone or Android emulator:

The starter app showing a plain bio card

It'south non bad, but the acme of the card doesn't have much pizazz. You lot'll change that throughout the tutorial.

Exploring the Project

Take a quick wait at the project structure. Aggrandize starsofscience package and cheque out the folders within:

Here's a breakdown of the folders:

  • utils contains four files with extension functions y'all'll use in your painting journey.
  • view contains CircularImageView which y'all'll use to display the avatar in a circular shape. The code within this class is out of the scope of this tutorial.
  • starsofscience contains three files:
    • MainActivity.kt is the app'southward principal and luncher activeness.
    • Painter.kt contains pigment() which you'll implement to paint your custom shape. You'll add all drawing and painting logic to this role.
    • CustomPainter.kt is a custom Android View with a constructor accepting the width and height of your custom shape in addition to a painter object that has all the cartoon and painting logic. This CustomPainter overrides onDraw() and delegates all the cartoon to the painter by executing sail?.let(painter::paint).

Now that you know more near the classes you'll work with accept a moment to acquire some of the theory behind making cute shapes.

Coding Your Shapes

Earlier diving into cartoon with Android Canvas, you need to know which tools y'all'll demand, how to utilise them and how to set up to lawmaking your target shape.

Remember nigh drawing in the physical world. To draw a shape, you demand to get a pencil and paper and and so use your mitt to movement the pencil across the newspaper's surface. Finally, if you desire to make it beautiful, you demand to become a brush with some paint.

In this department, y'all'll showtime by cartoon a shape freehand. Take hold of a pencil and paper and get ready!

Know Your Canvas

Your canvas acts as the digital version of the slice of newspaper yous depict on. It holds all your cartoon elements, including lines, curves, arches, shapes, text and images.

The canvas needs a size, including width and height. Drawing on a sheet without knowing its size can atomic number 82 to unexpected results.

On your newspaper, before cartoon whatsoever shape, ascertain the canvas by drawing a rectangle of any size you want. Any shapes you draw later will exist relative to that canvas.

Rectangle on paper

Notation: You don't want your shapes to accept an absolute position or size. Instead, brand them relative to the size of the canvas. This lets you brandish your shapes on different devices with unlike screen sizes.

For case, y'all might place your shape at the center of the canvas or make its size equal to half of the canvas size.

Now that you accept a canvas, it'due south time to create a shape.

Defining How to Move Your Pencil

In visual arts, yous have to move your pencil properly beyond the paper'due south surface to create your artwork. Y'all'll use the same mechanism to draw on the canvas.

Before you lot tin draw a shape, you need to consider which functionalities the canvass object needs to take.

For instance, if yous want to describe a square, yous need to draw four lines. So, yous demand the drawing line function in your framework. On the other hand, if you want to draw a crescent, you need to draw ii curves with the cartoon curve office.

Pick upwardly your pencil and draw a circle in the center of the circle that'south a quarter of the width, similar this:

Draw a circle on paper

Now, to convert that shape on your newspaper into a shape in Android, you need to consider its coordinates.

Computing Coordinates

Coordinates are pairs of numbers that define the exact location of a point on a plane.

Earlier you lot draw annihilation, you need to know the main points that make up that shape. For good practice, summate all the coordinates and dimensions on your paper before writing any code. This saves you coding fourth dimension and makes you lot focus on translating that shape from the paper onto your device.

Since y'all already drew a circle relative to the sail on your newspaper, y'all already calculated 2 things:

  1. The center of the circumvolve: Since your circle is at the middle of the sheet, the center of the circle is the center of the canvas. And then the x coordinate of the circle'due south middle is equal to half of the width of the canvas and the y coordinate of the circumvolve's center is equal to one-half of the height of the canvas. This means that:
    cx = canvas width / 2
    cy = canvas summit / ii
  2. The radius: Since your circle is a quarter of the canvas width, the bore of the circle is equal to a quarter of the width of the canvas. The radius is equal to half of the diameter. That means:
    diameter = canvas width / iv
    radius = diameter / 2 = canvass width / 8

Circle properties

See, cartoon your shapes on paper helps yous calculate the points you need to draw your shape relative to the canvas.

It's efficient to visualize what you lot need to do before information technology's fourth dimension to translate your ideas into lawmaking. Making paper sketches is a prerequisite for your custom drawing! :]

Using CustomPainter

Now that y'all've learned some theory, it'south fourth dimension to start using the Android Canvas and add together some code that volition reproduce your drawing in the app.

Implementing the Painter Interface

Get-go by creating a new class ProfileCardPainter in the starsofscience package. So supervene upon the whole file content with:

package com.raywenderlich.android.starsofscience  import android.graphics.* import androidx.annotation.ColorInt  //ane class ProfileCardPainter(     //2     @ColorInt private val colour: Int ) : Painter {   //three   override fun paint(sail: Canvas) {    } }        

Here you:

  1. Define a new class named ProfileCardPainter that implements the interface Painter.
  2. Then in its primary constructor you ascertain the contour color equally a grade holding.
  3. Finally, yous implement paint(canvas: Canvas). CustomPainter will phone call this method whenever the object needs to paint.

    You'll write all your drawing code within this function, which gives yous one parameter: The canvass to draw on.

Rendering With CustomPainter

Get to MainActivity.kt. You'll find the following line of lawmaking in onCreate():

profileCardContainer.setBackgroundColor(R.color.colorPrimary.toColorInt(this))        

It sets a background color to the profileCardContainer which is a FrameLayout already divers in XML. You don't need that line anymore because you lot want to add your custom shape instead of that solid colour.

Replace that line with the following code:

//one val azureColor = R.color.colorPrimary.toColorInt(this) val avatarRadius = R.dimen.avatar_radius.resToPx(this) val avatarMargin = R.dimen.avatar_margin.resToPx(this) val cardWidth = ViewGroup.LayoutParams.MATCH_PARENT val cardHeight = R.dimen.profile_card_height.resToPx(this).toInt() //2 val painter = ProfileCardPainter(   color = azureColor ) //3 profileCardContainer.addView(   CustomPainter(     context = this,     width = cardWidth,     height = cardHeight,     painter = painter   ) )        

Add whatsoever missing import by pressing Option+Enter on Mac or Alt+Enter on PC.

In the code above:

  1. Yous define the properties of your custom shape: Color, avatar radius, avatar margin, width and height.
  2. Then, you lot create a ProfileCardPainter with the color y'all previously defined.
  3. Finally, you add a new CustomPainter as a subview of profileCardContainer by passing all its needed properties:
    • context to create this custom Android View.
    • width and meridian of the custom shape.
    • painter responsible for all the drawing logic.

Build and run the app to see… a pretty ugly menu considering you haven't drawn anything yet. Don't worry, you'll beginning drawing something in a moment. :]

Initial changes to the app

Drawing Your First Shape

In this section, y'all'll practice with the tools you need to depict in the reckoner graphics world. They're a lot similar the physical tools you used to depict a circle on a paper. Then, with this knowledge, you'll draw your first shape!

Note: Graphics libraries have similar APIs for cartoon, which makes drawing in Android comparable to drawing in iOS, Palpitate and the web. When you lot primary drawing custom shapes on one platform, it's easy to reuse this knowledge on other platforms.

Drawing and Painting a Rectangle

To draw a rectangle, you demand to create a RectF object with the size y'all want. You and so need a Paint object with the colour you lot prefer to offset cartoon that RectF on the canvas.

RectF is a simple class with four immutable float backdrop: Left, pinnacle, correct and bottom. These four numbers represent a rectangle, where:

  • Left is the left-most indicate on the x-axis.
  • Superlative is the top-most point on the y-axis.
  • Right is the right-nearly point on the x-axis.
  • Bottom is the lesser-most point on the y-axis.

Note: Yous can summate any actress properties in RectF, like the width and height, based on these four main properties.

In this tutorial, you lot'll rely on RectF for your shape bounds. You'll depict each shape within of and based on a certain RectF.

In ProfileCardPainter.kt, go to paint() and add the post-obit:

//1 val width = sheet.width.toFloat() val height = canvas.superlative.toFloat() //2 val shapeBounds = RectFFactory.fromLTWH(0f, 0f, width, top) //3 val paint = Paint() paint.colour = color //4 sail.drawRect(shapeBounds, paint)        

Add any missing import by pressing Selection+Enter on Mac or Alt+Enter on PC.

Here's what this lawmaking defines:

  1. The width and meridian of the canvas.
  2. shapeBounds is a RectF with a size that fits the whole area of the canvas by using the factory office fromLTWH().
  3. paint is your paint and its color.
  4. Finally, you lot draw your shapeBounds on the sheet past passing it to drawRect() along with your paint from the previous line.

Now, build and run the app. Encounter that the card now has a blue rectangle as its background. Hooray, you've fatigued your first shape! :]

The starter app showing a plain bio card

That's ameliorate, merely there'south notwithstanding much room for improvement!

Using a Path to Draw the Profile Carte

A path is not a bitmap or raster, and it doesn't have pixels. Information technology's an outline that represents a series of smooth lines, arcs or Bézier curves. Using a path makes your shapes scalable and contained of the screen's resolution.

Path is a powerful class that y'all can use in many situations. For instance, yous tin clip a bitmap past a path, or y'all can use a path to draw a custom shape like you lot're about to practise right now.

Drawing the Profile Card

In this department, you lot'll get-go using the Path class to draw a more complex shape like the blueish shape here:

Profile card shape

Simply earlier you start, you lot need to do some training.

There are a few things you should note in the previous image:

  • Blackness dashed rectangle: Represents the whole sail.
  • Red dashed rectangle: Marks the premises of the blue shape. Information technology has the aforementioned width and height as the canvas, except that you lot subtract the avatar radius from its height.
  • Blue shape: A rectangle with a one-half circle, an arc of a circle, as a negative space at the lesser centre. This arc should accept a radius equal to the radius of the avatar.

Notation: An arc is a segment of a curve. In this case, the arc yous'll utilize is a section of a circumvolve's circumference, also called a circular arc.

The prototype below shows a blueish arc that starts at the goose egg degree bending and sweeps to xc degrees.

90 degrees arc

First, get the radius of the avatar. Commencement by calculation a new class property called avatarRadius to your ProfileCardPainter primary constructor:

grade ProfileCardPainter(     @ColorInt private val color: Int,     private val avatarRadius: Float ) : Painter {        

Then, become to MainActivity.kt and, in onCreate(), laissez passer the avatarRadius to ProfileCardPainter:

val painter = ProfileCardPainter(   colour = azureColor,   avatarRadius = avatarRadius )        

Finally, return to ProfileCardPainter.kt and update the shapeBounds by subtracting the avatarRadius from its top in fromLTWH():

val shapeBounds = RectFFactory.fromLTWH(0f, 0f, width, height - avatarRadius)        

The avatar radius

To come across the results build and run the app:

Initial results for your custom painter

Great! At present the bluish background stops halfway down the length of the avatar.

Adding Negative Space Around the Avatar

Side by side, you'll add together some negative space to the blue shape to set it autonomously from the avatar. Add a new office chosen drawBackground() to ProfileCardPainter:

            private fun drawBackground(sail: Canvas, bounds: RectF, avatarBounds: RectF) {   //i   val paint = Paint()   paint.color = colour    //two   val backgroundPath = Path().apply {     // three     moveTo(premises.left, premises.top)     // iv     lineTo(bounds.bottomLeft.x, premises.bottomLeft.y)     // 5     lineTo(avatarBounds.centerLeft.x, avatarBounds.centerLeft.y)     // 6     arcTo(avatarBounds, -180f, 180f, faux)     // 7     lineTo(bounds.bottomRight.x, premises.bottomRight.y)     // 8     lineTo(bounds.topRight.x, premises.topRight.y)     // 9     close()    }    //10   canvass.drawPath(backgroundPath, paint); }        

Add any missing import by pressing Choice+Enter on Mac or Alt+Enter on PC. To import all the extension functions yous need for RectF in a row, add the following import:

import com.raywenderlich.android.starsofscience.utils.*        

This diagram illustrates the proper coordinates for each point you need to build the path.

Custom path

In the previous code:

  1. Yous create a Paint object and set its color.
  2. Then, you create a Path object.
  3. You movement to the top-left corner, P1, without cartoon a line. This is similar moving a pencil to a starting point without touching the newspaper.
  4. Next, y'all add a straight line that starts at P1 and ends at P2.
  5. And so, you add a straight line that starts at P2 and ends at P3: The point at the edge of where you volition start drawing the arc.
  6. And then, starting from P3, add an arc in the upper one-half region of the avatar premises: The arc starts from the bending -180 degrees and sweeps past 180 degrees catastrophe at P4.
    You pass fake as the last parameter to prevent starting a new sub-path for the arc. This tells Android that you want the arc on the same path.
  7. Next, you lot add together a straight line that starts from the current point and ends at P5 at the lesser-right corner.
  8. Yous end by calculation a straight line that starts from the current point P5 and ends at the given betoken P6 at the top-right corner.
  9. Then y'all close the path past adding a straight line that starts at the current point P6 and ends at the beginning point on the path, P1.
  10. Finally, yous draw the backgroundPath on the canvas by passing it to drawPath() with paint.

In the previous code, you can collapse lines v and vi in a single line. Do you know how? Yous can find the solution in the spoiler below.

[spoiler title="Solution"]
You can plummet lines 5 and six by leaving only line six.

arcTo(avatarBounds, -180f, 180f, false)

The official documentation of

arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)

states: "If the beginning of the path is different from the path's current terminal point, then an automatic lineTo() is added to connect the current contour to the start of the arc."
[/spoiler]

Phew! That was a lot of code, but information technology was worth the effort!

Creating the Rectangle Effectually the Avatar

In ProfileCardPainter, go to paint() and replace the final 3 lines:

          val paint = Pigment()     paint.color = colour     canvas.drawRect(shapeBounds, paint)        

with the following code to create a new RectF around the avatar:

//ane val centerAvatar = PointF(shapeBounds.centerX(), shapeBounds.lesser) //two val avatarBounds = RectFFactory.fromCircle(center = centerAvatar, radius = avatarRadius) //three drawBackground(sheet, shapeBounds, avatarBounds)        

Here'due south what this code does:

  1. You create a PointF object for the heart point of the avatar, where x is the shapeBounds.centerX() and y is the bottom of the shapeBounds.
  2. And so, yous create a RectF object from the avatar circumvolve using fromCircle(). The center is centerAvatar, which you just created, and the radius is the avatarRadius.
  3. Finally, you telephone call drawBackground() and pass the canvas with balance of the parameters to draw your offset path.

Build and run the app. You lot'll encounter this:

Initial results for your custom painter

You lot probably don't notice the deviation nevertheless. Don't worry, you'll fix that adjacent.

Calculation a Margin Around the Avatar

In that location is a difference, simply you can't see it because the negative space is exactly equal to the circular avatar's size. Next, you'll brand that negative space a bit bigger to exit a margin betwixt information technology and the avatar.

First, become the margin of the avatar. Outset by calculation one more than class property called avatarMargin to your ProfileCardPainter primary constructor, don't forget the comma at the finish of the line above the new code.

class ProfileCardPainter(     ...     private val avatarMargin: Float )        

Then, go to MainActivity.kt and, in onCreate(), pass the avatarMargin to the ProfileCardPainter constructor:

val painter = ProfileCardPainter(   ...   avatarMargin = avatarMargin )        

Finally, return to ProfileCardPainter.kt and\where you create the avatarBounds in paint, add together .inflate(avatarMargin) to the end:

val avatarBounds = RectFFactory.fromCircle(center = centerAvatar, radius = avatarRadius).inflate(avatarMargin)        

Calling inflate() on a RectF creates a new RectF object whose left, top, right and bottom edges are moved outwards by the given value. The result is a nice space effectually the avatar.

To see the margin in action, build and run the app.

Margin around the avatar

Pretty... but ordinary. Side by side, you'll spice up the background past calculation an attractive curved shape.

Calculation More Corking Shapes

To enhance your custom shape, yous tin can add together some unproblematic decorations like stars or circles in a partially-faded color. For this app, you'll add together a more interesting ornament: A curvy shape in slope colors.

Adding a Curved Shape

Before you start drawing, take a moment to learn about the different types of curves. The Quadratic Bézier Curve and the Cubic Bézier Bend are two commonly used curves.

  • A quadratic Bézier curve requires three points to draw: A beginning point, an endpoint and a handle point that pulls the curve towards it.

    Quadratic Bézier Curve

  • A cubic Bézier curve needs four points to draw: A beginning point, an end point and two handle points that pull the curve towards them.

    Cubic Bézier Curve

Side by side, you'll employ a quadratic Bézier curve to create an interesting background shape.

Cartoon a Quadratic Bézier Curve

Start by creating a new function called drawCurvedShape() inside ProfileCardPainter with the post-obit:

private fun drawCurvedShape(sheet: Canvas, bounds: RectF, avatarBounds: RectF) {   //i   val paint = Pigment()   pigment.colour = colour.darkerShade()    //two   val handlePoint = PointF(bounds.left + (premises.width() * 0.25f), bounds.meridian)    //3   val curvePath = Path().apply {     //4     moveTo(premises.bottomLeft.x, premises.bottomLeft.y)     //5     lineTo(avatarBounds.centerLeft.ten, avatarBounds.centerLeft.y)     //half dozen     arcTo(avatarBounds, -180f, 180f, false)     //7     lineTo(bounds.bottomRight.x, bounds.bottomRight.y)     //viii     lineTo(premises.topRight.10, bounds.topRight.y)     //9     quadTo(handlePoint.x, handlePoint.y, bounds.bottomLeft.x, premises.bottomLeft.y)     //x     close()   }    //xi   canvas.drawPath(curvePath, pigment) }        

This diagram will aid you understand the lawmaking you added. Use it as a guide to the proper coordinates for each signal yous'll build to create the path:

Path for new arc

In the previous lawmaking:

  1. You create a Pigment object and set its color to a darker shade of the profile color.
  2. Then, you create a handle point at the top left corner of the RectF, shifted to the right by 25% of the width of the RectF. This is P6 in the guide image.
  3. You create a Path object.
  4. Then, you move to the lesser-left corner, P1 in the guide image.
  5. You add a straight line that starts from P1 and ends at P2: The middle bespeak at the edge of the black dashed avatar premises RectF.
  6. And so, starting from the current point, P2, add an arc in the upper- half region of the avatar bounds: The arc starts from the angle -180 degrees and sweeps by 180 degrees ending in P3.
    You pass imitation as the concluding parameter so you don't start a new sub-path for the arc. This tells Android that yous desire the arc on the aforementioned path.
  7. You add together a straight line that starts from the current point and ends at the given point, the bottom-right corner. This adds a line from P3 to P4.
  8. And then, you add a straight line that starts from the current point and ends at the given point, the top-right corner, adding a line from P4 to P5.
  9. You add together a quadratic Bézier bend that starts from the current betoken, P5, and ends at the bottom-left corner, P1, using the handle point you created in step two.
  10. Finally, you shut the path, even though it's non required this fourth dimension since you are back at the offset point on the path.
  11. You draw curvePath on the canvas by passing it to drawPath() forth with the pigment object.

Finalizing the Curve

You're almost finished creating the bend. In ProfileCardPainter, become to the terminal line in paint() and add the following code:

//1 val curvedShapeBounds = RectFFactory.fromLTRB(     shapeBounds.left,     shapeBounds.top + shapeBounds.height() * 0.35f,     shapeBounds.right,     shapeBounds.bottom ) //two drawCurvedShape(canvas, curvedShapeBounds, avatarBounds)        

Here, yous:

  1. Create a RectF that is similar to the shapeBounds rect, except y'all've shifted its peak slightly to the bottom by 35% of the shapeBounds' top: This is the red dashed RectF in the epitome in a higher place.
  2. Call drawCurvedShape() and laissez passer the sail object, the curved shape premises and the avatar bounds to information technology.

Build and run the app to run across the bully background curve behind the avatar:

Add a curve behind the avatar

Then you're done, right? Almost. In that location'due south nonetheless one more finishing touch you need to add.

Adding Gradient Pigment

You lot've created your offset beautiful, custom curved shape, but your graphic designer wants yous to exercise one more thing: Add slope colors to your curved shape.

There are different types of shaders or gradients, including linear gradients, which transition through at least 2 colors in a direct line, and radial gradients, which transition through colors starting from a cardinal bespeak and radiating outward.

Right now, you lot'll create a shader, a linear gradient described past three colors. Each color needs a end to specify its position on a line from 0.0 to 1.0.

Offset by creating a new function called createGradient() within ProfileCardPainter with the following code:

            private fun createGradient(bounds: RectF): LinearGradient {   //1   val colors = intArrayOf(color.darkerShade(), color, color.darkerShade())   //2   val stops = floatArrayOf(0.0f, 0.3f, one.0f)   //iii   render LinearGradient(       premises.centerLeft.x, bounds.centerLeft.y,       bounds.centerRight.x, bounds.centerRight.y,       colors,       stops,       Shader.TileMode.Echo   ) }        

Here's what's going on in this code:

  1. You create a listing of iii colors, where the middle color is the contour color and the kickoff and terminal colors are darker shades of that profile color.
  2. And then y'all create a list of three stops. The beginning is 0.0, which puts the corresponding color in the colors list at the zero position of the slope color. In the same way, the centre and the stops specify the positions of their corresponding colors in the color list.
  3. Finally, you create a linear gradient by passing the get-go coordinates and the end coordinates of the gradient with the given colors and stops, and the shader TileMode to echo the gradient in case the surface area which you make full is larger than the shader you created.

Now go to drawCurvedShape() and update the paint object to employ the new linear gradient instead of a solid color.

Replace this line:

paint.color = colour.darkerShade()        

With this one:

pigment.shader = createGradient(bounds)        

Hither, you create a new slope and set up it to the paint object.

Finally, build and run the app to see a gradient within the groundwork curve:

Background curve with gradient

Congratulations! You've created a cute profile card with an eye-catching custom background shape and shading.

Where to Go From Here?

Yous can download the completed final project using the Download Materials button at the tiptop or bottom of the tutorial.

Wow, that was a lot of work! But you learned a lot, as well. In addition to taking a deep wait at Canvas and many Android Graphics APIs, you learned how to:

  • Fix your custom shape on paper earlier coding.
  • Apply Path and how to add different lines to information technology sequentially.
  • Draw a curved shape in gradient colors.

To acquire more about Canvas and Android custom views check out the following links:

  • Android Custom View Tutorial.
  • Making your drawing Interactive.
  • Custom Android Compound View.

Likewise, you can bank check RichPath library. It's an open-source library that gives you total command over your custom shapes in vector drawable format so you lot tin can manipulate and animate them easily at runtime.

Feel complimentary to share your feedback, comments or ask questions in the forum below. Don't stop cartoon. ;]

woollacottshime1958.blogspot.com

Source: https://www.raywenderlich.com/9556022-drawing-custom-shapes-in-android