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 theCanvas
. - Draw a curved shape in gradient colors.
The custom shape you'll create will look like this:
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:
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
overridesonDraw()
and delegates all the cartoon to the painter by executingsail?.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.
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:
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:
- 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
- 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
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:
- Define a new class named
ProfileCardPainter
that implements the interfacePainter
. - Then in its primary constructor you ascertain the contour color equally a grade holding.
- 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:
- Yous define the properties of your custom shape: Color, avatar radius, avatar margin, width and height.
- Then, you lot create a
ProfileCardPainter
with the color y'all previously defined. - Finally, you add a new
CustomPainter
as a subview ofprofileCardContainer
by passing all its needed properties:-
context
to create this custom AndroidView
. -
width
andmeridian
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. :]
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:
- The
width
andmeridian
of the canvas. -
shapeBounds
is aRectF
with a size that fits the whole area of the canvas by using the factory officefromLTWH()
. -
paint
is your paint and its color. - Finally, you lot draw your
shapeBounds
on thesheet
past passing it todrawRect()
along with yourpaint
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! :]
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:
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.
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)
To come across the results build and run the app:
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.
In the previous code:
- Yous create a
Paint
object and set its color. - Then, you create a
Path
object. - 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.
- Next, y'all add a straight line that starts at P1 and ends at P2.
- 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.
- 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 past180
degrees catastrophe at P4.
You passfake
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. - Next, you lot add together a straight line that starts from the current point and ends at P5 at the lesser-right corner.
- 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.
- 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.
- Finally, yous draw the
backgroundPath
on the canvas by passing it todrawPath()
withpaint
.
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:
- You create a
PointF
object for the heart point of the avatar, wherex
is theshapeBounds.centerX()
andy
is thebottom
of theshapeBounds
. - And so, yous create a
RectF
object from the avatar circumvolve usingfromCircle()
. The center iscenterAvatar
, which you just created, and the radius is theavatarRadius
. - 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:
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.
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.
- 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.
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:
In the previous lawmaking:
- You create a
Pigment
object and set its color to a darker shade of the profile color. - Then, you create a handle point at the top left corner of the
RectF
, shifted to the right by 25% of the width of theRectF
. This is P6 in the guide image. - You create a
Path
object. - Then, you move to the lesser-left corner, P1 in the guide image.
- 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
. - 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 by180
degrees ending in P3.
You passimitation
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. - 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.
- 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.
- 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.
- 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.
- You draw
curvePath
on the canvas by passing it todrawPath()
forth with thepigment
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:
- Create a
RectF
that is similar to theshapeBounds
rect, except y'all've shifted its peak slightly to the bottom by 35% of theshapeBounds
' top: This is the red dashedRectF
in the epitome in a higher place. - Call
drawCurvedShape()
and laissez passer thesail
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:
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:
- 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.
- 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.
- 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:
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