Design and Implement an Application That Draws a Circle
Did you ever want to create highly-customized user interfaces in Android? Then this is the tutorial for you!
To draw custom shapes, you need to keep iterating until you achieve the cute art you want. In this tutorial, you'll learn how to depict your design on paper first to avoid wasting time via trial and error.
You'll improve an app called Stars of Science. You'll learn how to create custom shapes past painting a profile carte du jour with a curved custom shape and slope colors.
Throughout the tutorial, you'll acquire how to:
- Prepare a custom shape on newspaper before coding.
- Extend the Android
Viewto draw and paint information technology on theSheet. - Draw a curved shape in gradient colors.
The custom shape you lot'll create will expect like this:
Note: This tutorial assumes you understand the basics of Android development with Kotlin. If you're new to Android evolution, please go through Kickoff Android Development with Kotlin to understand the nuts. If you lot're new to Kotlin, bank check out this Introduction to Kotlin tutorial.
Getting Started
Download the projection materials by clicking the Download Materials button at the tiptop or bottom of this tutorial. Launch Android Studio 3.6.1 or later on and select Open an existing Android Studio projection. Then navigate to and select the starter project folder where you lot'll find the files you need to start, along with some widgets.
Your app already has its bones UI set so you can focus on drawing custom shapes in Android.
Build and run the app. You'll see the following screen on your mobile telephone or Android emulator:
It's great, but the top of the card doesn't have much pizazz. You lot'll change that throughout the tutorial.
Exploring the Project
Take a quick await at the projection structure. Expand starsofscience package and check out the folders inside:
Here's a breakdown of the folders:
- utils contains four files with extension functions you'll use in your painting journeying.
- view contains
CircularImageViewwhich you lot'll use to display the avatar in a circular shape. The code inside this class is out of the telescopic of this tutorial. - starsofscience contains three files:
- MainActivity.kt is the app's main and luncher activity.
- Painter.kt contains pigment() which you'll implement to paint your custom shape. You'll add together all drawing and painting logic to this function.
- 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 drawing and painting logic. This
CustomPainteroverridesonDraw()and delegates all the drawing to the painter past executingcanvas?.allow(painter::pigment).
Now that you lot know more nigh the classes you'll work with take a moment to learn some of the theory backside making beautiful shapes.
Coding Your Shapes
Before diving into drawing with Android Sheet, you need to know which tools y'all'll need, how to use them and how to prepare to lawmaking your target shape.
Think about drawing in the concrete world. To draw a shape, y'all need to become a pencil and paper and and then use your hand to move the pencil across the newspaper'south surface. Finally, if you desire to make information technology beautiful, you need to get a castor with some paint.
In this department, y'all'll outset by drawing a shape freehand. Take hold of a pencil and paper and get fix!
Know Your Sheet
Your canvas acts equally the digital version of the piece of paper you lot depict on. It holds all your drawing elements, including lines, curves, arches, shapes, text and images.
The sheet needs a size, including width and peak. Cartoon on a canvas without knowing its size tin can atomic number 82 to unexpected results.
On your newspaper, before drawing any shape, ascertain the canvas by drawing a rectangle of any size you desire. Whatsoever shapes yous draw later volition exist relative to that canvas.
Note: Yous don't want your shapes to take an absolute position or size. Instead, make them relative to the size of the sheet. This lets you display your shapes on different devices with dissimilar screen sizes.
For instance, yous might place your shape at the center of the sail or make its size equal to half of the canvas size.
Now that you have a sheet, information technology'southward time to create a shape.
Defining How to Move Your Pencil
In visual arts, yous have to motion your pencil properly across the paper's surface to create your artwork. You'll use the same mechanism to describe on the canvass.
Before yous can draw a shape, you demand to consider which functionalities the canvas object needs to take.
For instance, if you want to draw a foursquare, you need to depict four lines. So, you need the drawing line office in your framework. On the other manus, if you desire to depict a crescent, you need to draw two curves with the drawing curve function.
Selection up your pencil and draw a circumvolve in the center of the circle that's a quarter of the width, similar this:
Now, to convert that shape on your paper into a shape in Android, you demand to consider its coordinates.
Calculating Coordinates
Coordinates are pairs of numbers that ascertain the exact location of a point on a airplane.
Before you lot draw anything, you need to know the main points that make upward that shape. For expert practice, summate all the coordinates and dimensions on your paper before writing any code. This saves you coding fourth dimension and makes you focus on translating that shape from the paper onto your device.
Since you lot already drew a circle relative to the canvas on your paper, you already calculated 2 things:
- The middle of the circumvolve: Since your circle is at the center of the canvas, the heart of the circumvolve is the center of the canvass. So the ten coordinate of the circumvolve'due south center is equal to half of the width of the sail and the y coordinate of the circle'south center is equal to half of the summit of the canvas. This means that:
cx = sail width / 2
cy = canvas summit / 2 - The radius: Since your circle is a quarter of the canvas width, the diameter of the circle is equal to a quarter of the width of the canvas. The radius is equal to one-half of the diameter. That ways:
diameter = canvas width / iv
radius = diameter / two = canvas width / 8
See, drawing your shapes on paper helps you lot calculate the points you need to draw your shape relative to the canvas.
It's efficient to visualize what you need to do before it's time to translate your ideas into code. Making paper sketches is a prerequisite for your custom cartoon! :]
Using CustomPainter
Now that you've learned some theory, it's time to start using the Android Sail and add some code that will reproduce your cartoon in the app.
Implementing the Painter Interface
Commencement by creating a new class ProfileCardPainter in the starsofscience packet. Then supersede the whole file content with:
package com.raywenderlich.android.starsofscience import android.graphics.* import androidx.annotation.ColorInt //one course ProfileCardPainter( //two @ColorInt individual val color: Int ) : Painter { //3 override fun paint(canvas: Canvas) { } } Here you:
- Define a new form named
ProfileCardPainterthat implements the interfacePainter. - Then in its primary constructor you define the contour color every bit a class holding.
- Finally, you implement
paint(canvas: Canvas).CustomPainterwill call this method whenever the object needs to paint.You'll write all your cartoon code inside this function, which gives yous one parameter: The
canvasto draw on.
Rendering With CustomPainter
Become to MainActivity.kt. You'll find the following line of code in onCreate():
profileCardContainer.setBackgroundColor(R.color.colorPrimary.toColorInt(this)) Information technology sets a background color to the profileCardContainer which is a FrameLayout already defined in XML. You don't need that line anymore because you want to add your custom shape instead of that solid color.
Replace that line with the following lawmaking:
//i 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() //ii val painter = ProfileCardPainter( color = azureColor ) //3 profileCardContainer.addView( CustomPainter( context = this, width = cardWidth, height = cardHeight, painter = painter ) ) Add whatever missing import past pressing Selection+Enter on Mac or Alt+Enter on PC.
In the code above:
- You lot define the backdrop of your custom shape: Colour, avatar radius, avatar margin, width and top.
- Then, you create a
ProfileCardPainterwith the color you previously divers. - Finally, yous add a new
CustomPainteras a subview ofprofileCardContainerpast passing all its needed properties:-
contextto create this custom AndroidView. -
widthandheightof the custom shape. -
painterresponsible for all the cartoon logic.
-
Build and run the app to see… a pretty ugly card because you oasis't fatigued anything all the same. Don't worry, you'll get-go drawing something in a moment. :]
Drawing Your First Shape
In this section, you'll do with the tools you demand to draw in the computer graphics world. They're a lot like the concrete tools you used to depict a circle on a paper. So, with this knowledge, you lot'll describe your first shape!
Note: Graphics libraries have similar APIs for drawing, which makes drawing in Android comparable to drawing in iOS, Flutter and the web. When you master drawing custom shapes on one platform, it'due south easy to reuse this knowledge on other platforms.
Drawing and Painting a Rectangle
To draw a rectangle, yous need to create a RectF object with the size y'all want. You lot then demand a Paint object with the color y'all prefer to outset drawing that RectF on the canvass.
RectF is a simple grade with four immutable float properties: Left, top, correct and bottom. These four numbers correspond a rectangle, where:
- Left is the left-almost indicate on the x-centrality.
- Top is the superlative-most point on the y-axis.
- Right is the right-well-nigh signal on the ten-axis.
- Bottom is the bottom-most point on the y-centrality.
Note: You tin can calculate any actress properties in RectF, like the width and height, based on these four primary properties.
In this tutorial, you lot'll rely on RectF for your shape bounds. Yous'll draw each shape within of and based on a certain RectF.
In ProfileCardPainter.kt, get to paint() and add the following:
//1 val width = canvas.width.toFloat() val meridian = canvas.height.toFloat() //two val shapeBounds = RectFFactory.fromLTWH(0f, 0f, width, pinnacle) //3 val pigment = Paint() paint.color = color //4 canvas.drawRect(shapeBounds, pigment) Add whatever missing import by pressing Choice+Enter on Mac or Alt+Enter on PC.
Here's what this code defines:
- The
widthandpinnacleof the sheet. -
shapeBoundsis aRectFwith a size that fits the whole area of the canvas by using the manufactory officefromLTWH(). -
pigmentis your paint and its colour. - Finally, y'all draw your
shapeBoundson thecanvasby passing it todrawRect()along with yourpaintfrom the previous line.
At present, build and run the app. Encounter that the card now has a blue rectangle as its background. Hooray, you've drawn your first shape! :]
That'south better, but at that place'south still much room for improvement!
Using a Path to Depict the Contour Menu
A path is non a bitmap or raster, and it doesn't have pixels. It'southward an outline that represents a serial of smooth lines, arcs or Bézier curves. Using a path makes your shapes scalable and independent of the screen's resolution.
Path is a powerful course that you tin can use in many situations. For example, you tin prune a bitmap by a path, or you can utilize a path to draw a custom shape like you're about to do right at present.
Drawing the Profile Carte du jour
In this section, you'll start using the Path form to draw a more complex shape like the blue shape here:
But before you lot start, you lot need to do some preparation.
There are a few things you should notation in the previous image:
- Black dashed rectangle: Represents the whole sheet.
- Red dashed rectangle: Marks the premises of the blue shape. It has the same width and top as the canvas, except that you subtract the avatar radius from its height.
- Blue shape: A rectangle with a half circle, an arc of a circle, as a negative space at the bottom center. This arc should accept a radius equal to the radius of the avatar.
Note: An arc is a segment of a curve. In this case, the arc yous'll use is a department of a circle's circumference, also chosen a round arc.
The image below shows a blueish arc that starts at the zip degree angle and sweeps to 90 degrees.
Outset, get the radius of the avatar. Beginning by adding a new class belongings called avatarRadius to your ProfileCardPainter principal constructor:
class ProfileCardPainter( @ColorInt private val color: Int, private val avatarRadius: Float ) : Painter { Then, become to MainActivity.kt and, in onCreate(), pass the avatarRadius to ProfileCardPainter:
val painter = ProfileCardPainter( color = azureColor, avatarRadius = avatarRadius ) Finally, return to ProfileCardPainter.kt and update the shapeBounds by subtracting the avatarRadius from its height in fromLTWH():
val shapeBounds = RectFFactory.fromLTWH(0f, 0f, width, top - avatarRadius)
To see the results build and run the app:
Corking! At present the blue background stops halfway down the length of the avatar.
Adding Negative Space Around the Avatar
Adjacent, you'll add some negative space to the bluish shape to prepare information technology apart from the avatar. Add a new function chosen drawBackground() to ProfileCardPainter:
individual fun drawBackground(canvass: Canvas, bounds: RectF, avatarBounds: RectF) { //1 val paint = Paint() paint.color = colour //ii val backgroundPath = Path().apply { // 3 moveTo(bounds.left, bounds.top) // 4 lineTo(bounds.bottomLeft.ten, premises.bottomLeft.y) // 5 lineTo(avatarBounds.centerLeft.x, avatarBounds.centerLeft.y) // 6 arcTo(avatarBounds, -180f, 180f, false) // 7 lineTo(bounds.bottomRight.x, bounds.bottomRight.y) // viii lineTo(bounds.topRight.x, bounds.topRight.y) // ix shut() } //x sheet.drawPath(backgroundPath, paint); } Add any missing import by pressing Pick+Enter on Mac or Alt+Enter on PC. To import all the extension functions y'all need for RectF in a row, add together the following import:
import com.raywenderlich.android.starsofscience.utils.* This diagram illustrates the proper coordinates for each indicate you need to build the path.
In the previous code:
- Yous create a
Paintobject and set its color. - So, you create a
Pathobject. - You move to the pinnacle-left corner, P1, without drawing a line. This is like moving a pencil to a starting point without touching the newspaper.
- Side by side, you add together a direct line that starts at P1 and ends at P2.
- Then, you add a directly line that starts at P2 and ends at P3: The point at the border of where you lot will start drawing the arc.
- Then, starting from P3, add an arc in the upper half region of the avatar bounds: The arc starts from the angle
-180degrees and sweeps by180degrees ending at P4.
You passfalseas the last parameter to prevent starting a new sub-path for the arc. This tells Android that you want the arc on the aforementioned path. - Next, you lot add a direct line that starts from the current point and ends at P5 at the bottom-right corner.
- Y'all finish past adding a direct line that starts from the current betoken P5 and ends at the given point P6 at the superlative-correct corner.
- And so you lot close the path by adding a direct line that starts at the current point P6 and ends at the offset point on the path, P1.
- Finally, you draw the
backgroundPathon the sheet by passing it todrawPath()withpigment.
In the previous code, yous can collapse lines five and six in a single line. Do you know how? You can find the solution in the spoiler below.
[spoiler title="Solution"]
You lot can plummet lines five and six past 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 start of the path is different from the path'southward current last 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, merely it was worth the endeavor!
Creating the Rectangle Around the Avatar
In ProfileCardPainter, get to paint() and supplant the last 3 lines:
val pigment = Paint() paint.color = color canvas.drawRect(shapeBounds, pigment) with the post-obit code to create a new RectF effectually the avatar:
//1 val centerAvatar = PointF(shapeBounds.centerX(), shapeBounds.bottom) //2 val avatarBounds = RectFFactory.fromCircle(center = centerAvatar, radius = avatarRadius) //iii drawBackground(canvass, shapeBounds, avatarBounds) Here'due south what this code does:
- You create a
PointFobject for the center indicate of the avatar, wherexis theshapeBounds.centerX()andyis thebottomof theshapeBounds. - Then, you create a
RectFobject from the avatar circle usingfromCircle(). The center iscenterAvatar, which y'all merely created, and the radius is theavatarRadius. - Finally, you call
drawBackground()and pass the canvas with rest of the parameters to depict your start path.
Build and run the app. You'll encounter this:
You lot probably don't detect the difference notwithstanding. Don't worry, you'll fix that next.
Adding a Margin Around the Avatar
There is a difference, just you lot can't see information technology because the negative infinite is exactly equal to the circular avatar's size. Side by side, yous'll brand that negative space a bit bigger to leave a margin between information technology and the avatar.
First, become the margin of the avatar. Start by adding one more class holding chosen avatarMargin to your ProfileCardPainter primary constructor, don't forget the comma at the end of the line in a higher place the new code.
class ProfileCardPainter( ... private val avatarMargin: Float ) Then, go to MainActivity.kt and, in onCreate(), laissez passer the avatarMargin to the ProfileCardPainter constructor:
val painter = ProfileCardPainter( ... avatarMargin = avatarMargin ) Finally, return to ProfileCardPainter.kt and\where y'all create the avatarBounds in pigment , add .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, elevation, correct and bottom edges are moved outwards by the given value. The result is a prissy infinite around the avatar.
To run into the margin in activity, build and run the app.
Pretty... but ordinary. Next, you'll spice up the background past adding an bonny curved shape.
Calculation More Neat Shapes
To enhance your custom shape, yous can add together some uncomplicated decorations similar stars or circles in a partially-faded color. For this app, you'll add a more than interesting ornamentation: A curvy shape in gradient colors.
Adding a Curved Shape
Before you showtime cartoon, take a moment to learn about the different types of curves. The Quadratic Bézier Bend and the Cubic Bézier Curve are two commonly used curves.
- A quadratic Bézier curve requires three points to depict: A start point, an endpoint and a handle bespeak that pulls the curve towards it.
- A cubic Bézier curve needs four points to draw: A first point, an end bespeak and ii handle points that pull the curve towards them.
Next, you'll use a quadratic Bézier curve to create an interesting background shape.
Drawing a Quadratic Bézier Curve
Beginning past creating a new part called drawCurvedShape() inside ProfileCardPainter with the following:
private fun drawCurvedShape(canvas: Sail, bounds: RectF, avatarBounds: RectF) { //1 val pigment = Pigment() paint.color = color.darkerShade() //2 val handlePoint = PointF(bounds.left + (bounds.width() * 0.25f), bounds.height) //3 val curvePath = Path().apply { //4 moveTo(premises.bottomLeft.ten, bounds.bottomLeft.y) //5 lineTo(avatarBounds.centerLeft.10, avatarBounds.centerLeft.y) //half dozen arcTo(avatarBounds, -180f, 180f, simulated) //seven lineTo(bounds.bottomRight.ten, bounds.bottomRight.y) //8 lineTo(bounds.topRight.x, bounds.topRight.y) //9 quadTo(handlePoint.10, handlePoint.y, bounds.bottomLeft.ten, bounds.bottomLeft.y) //10 close() } //eleven canvas.drawPath(curvePath, paint) } This diagram will assist you understand the code yous added. Use it as a guide to the proper coordinates for each bespeak you'll build to create the path:
In the previous code:
- You create a
Paintobject and gear up its color to a darker shade of the contour colour. - So, you lot 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 paradigm. - Yous create a
Pathobject. - Then, yous movement to the bottom-left corner, P1 in the guide image.
- Yous add together a straight line that starts from P1 and ends at P2: The heart point at the edge of the black dashed avatar bounds
RectF. - And then, starting from the current indicate, P2, add an arc in the upper- half region of the avatar bounds: The arc starts from the angle
-180degrees and sweeps by180degrees ending in P3.
You passfalseas the last parameter and then you don't starting time a new sub-path for the arc. This tells Android that you want the arc on the same path. - You lot add a directly line that starts from the electric current point and ends at the given point, the lesser-right corner. This adds a line from P3 to P4.
- And then, yous add together a straight line that starts from the current signal and ends at the given point, the acme-right corner, adding a line from P4 to P5.
- Yous add a quadratic Bézier curve that starts from the current point, P5, and ends at the lesser-left corner, P1, using the handle point you created in step two.
- Finally, you close the path, even though it's not required this time since you are back at the start indicate on the path.
- You lot describe
curvePathon the canvas past passing information technology todrawPath()along with thepigmentobject.
Finalizing the Bend
Yous're about finished creating the curve. In ProfileCardPainter, go to the last line in paint() and add the following code:
//one val curvedShapeBounds = RectFFactory.fromLTRB( shapeBounds.left, shapeBounds.peak + shapeBounds.height() * 0.35f, shapeBounds.right, shapeBounds.bottom ) //2 drawCurvedShape(canvas, curvedShapeBounds, avatarBounds) Here, you:
- Create a
RectFthat is similar to theshapeBoundsrect, except y'all've shifted its meridian slightly to the lesser by 35% of theshapeBounds' tiptop: This is the crimson dashedRectFin the image to a higher place. - Telephone call
drawCurvedShape()and pass thecanvassobject, the curved shape bounds and the avatar premises to it.
Build and run the app to see the cracking background curve backside the avatar:
So you're washed, right? Almost. There'southward all the same one more finishing touch you demand to add.
Adding Gradient Paint
You've created your first beautiful, custom curved shape, simply your graphic designer wants you to practise one more affair: Add gradient colors to your curved shape.
There are different types of shaders or gradients, including linear gradients, which transition through at least two colors in a directly line, and radial gradients, which transition through colors starting from a central point and radiating outward.
Right at present, you lot'll create a shader, a linear gradient described by 3 colors. Each color needs a finish to specify its position on a line from 0.0 to i.0.
Start by creating a new function called createGradient() inside ProfileCardPainter with the post-obit code:
private fun createGradient(bounds: RectF): LinearGradient { //one val colors = intArrayOf(color.darkerShade(), colour, colour.darkerShade()) //2 val stops = floatArrayOf(0.0f, 0.3f, i.0f) //iii return LinearGradient( bounds.centerLeft.x, bounds.centerLeft.y, bounds.centerRight.x, bounds.centerRight.y, colors, stops, Shader.TileMode.REPEAT ) } Here's what's going on in this code:
- You create a list of three colors, where the middle color is the profile colour and the first and concluding colors are darker shades of that profile color.
- Then you lot create a list of 3 stops. The commencement is 0.0, which puts the corresponding color in the colors listing at the zero position of the slope color. In the same way, the middle and the stops specify the positions of their corresponding colors in the colour listing.
- Finally, you create a linear gradient past passing the start coordinates and the end coordinates of the gradient with the given colors and stops, and the shader TileMode to repeat the gradient in case the area which you fill up is larger than the shader y'all created.
Now go to drawCurvedShape() and update the paint object to utilise the new linear gradient instead of a solid colour.
Replace this line:
paint.colour = color.darkerShade() With this one:
pigment.shader = createGradient(premises) Here, you create a new slope and set information technology to the paint object.
Finally, build and run the app to see a gradient within the background bend:
Congratulations! You've created a beautiful profile card with an eye-catching custom background shape and shading.
Where to Go From Here?
You lot 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 y'all learned a lot, besides. In add-on to taking a deep expect at Sail and many Android Graphics APIs, you learned how to:
- Prepare your custom shape on newspaper before coding.
- Use
Pathand how to add different lines to it sequentially. - Draw a curved shape in gradient colors.
To learn more than nearly Sail and Android custom views bank check out the following links:
- Android Custom View Tutorial.
- Making your cartoon Interactive.
- Custom Android Compound View.
Also, you can cheque RichPath library. It's an open up-source library that gives you full control over your custom shapes in vector drawable format so y'all can manipulate and animate them easily at runtime.
Feel free to share your feedback, comments or enquire questions in the forum below. Don't stop drawing. ;]
Source: https://www.raywenderlich.com/9556022-drawing-custom-shapes-in-android
0 Response to "Design and Implement an Application That Draws a Circle"
ارسال یک نظر