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 View to draw and paint information technology on the Sheet.
  • Draw a curved shape in gradient colors.

The custom shape you lot'll create will expect like this:

Android custom shape

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:

The starter app showing a plain bio card

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 CircularImageView which 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 CustomPainter overrides onDraw() and delegates all the drawing to the painter past executing canvas?.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.

Rectangle on paper

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:

Draw a circle on paper

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:

  1. 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
  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

Circle properties

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:

  1. Define a new form named ProfileCardPainter that implements the interface Painter.
  2. Then in its primary constructor you define the contour color every bit a class holding.
  3. Finally, you implement paint(canvas: Canvas). CustomPainter will 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 canvas to 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:

  1. You lot define the backdrop of your custom shape: Colour, avatar radius, avatar margin, width and top.
  2. Then, you create a ProfileCardPainter with the color you previously divers.
  3. Finally, yous add a new CustomPainter as a subview of profileCardContainer past passing all its needed properties:
    • context to create this custom Android View.
    • width and height of the custom shape.
    • painter responsible 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. :]

Initial changes to the app

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:

  1. The width and pinnacle of the sheet.
  2. shapeBounds is a RectF with a size that fits the whole area of the canvas by using the manufactory office fromLTWH().
  3. pigment is your paint and its colour.
  4. Finally, y'all draw your shapeBounds on the canvas by passing it to drawRect() along with your paint from 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! :]

The starter app showing a plain bio card

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:

Profile card shape

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.

90 degrees arc

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)        

The avatar radius

To see the results build and run the app:

Initial results for your custom painter

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.

Custom path

In the previous code:

  1. Yous create a Paint object and set its color.
  2. So, you create a Path object.
  3. 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.
  4. Side by side, you add together a direct line that starts at P1 and ends at P2.
  5. 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.
  6. Then, starting from P3, 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 at P4.
    You pass false as 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.
  7. Next, you lot add a direct line that starts from the current point and ends at P5 at the bottom-right corner.
  8. 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.
  9. 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.
  10. Finally, you draw the backgroundPath on the sheet by passing it to drawPath() with pigment.

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:

  1. You create a PointF object for the center indicate of the avatar, where x is the shapeBounds.centerX() and y is the bottom of the shapeBounds.
  2. Then, you create a RectF object from the avatar circle using fromCircle(). The center is centerAvatar, which y'all merely created, and the radius is the avatarRadius.
  3. 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:

Initial results for your custom painter

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.

Margin around the avatar

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.

    Quadratic Bézier Curve

  • 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.

    Cubic Bézier Curve

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:

Path for new arc

In the previous code:

  1. You create a Paint object and gear up its color to a darker shade of the contour colour.
  2. 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 the RectF. This is P6 in the guide paradigm.
  3. Yous create a Path object.
  4. Then, yous movement to the bottom-left corner, P1 in the guide image.
  5. 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.
  6. 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 -180 degrees and sweeps by 180 degrees ending in P3.
    You pass false as 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.
  7. 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.
  8. 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.
  9. 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.
  10. Finally, you close the path, even though it's not required this time since you are back at the start indicate on the path.
  11. You lot describe curvePath on the canvas past passing information technology to drawPath() along with the pigment object.

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:

  1. Create a RectF that is similar to the shapeBounds rect, except y'all've shifted its meridian slightly to the lesser by 35% of the shapeBounds' tiptop: This is the crimson dashed RectF in the image to a higher place.
  2. Telephone call drawCurvedShape() and pass the canvass object, the curved shape bounds and the avatar premises to it.

Build and run the app to see the cracking background curve backside the avatar:

Add a curve behind 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:

  1. 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.
  2. 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.
  3. 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:

Background curve with gradient

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 Path and 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. ;]

smiththenealt.blogspot.com

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

0 Response to "Design and Implement an Application That Draws a Circle"

ارسال یک نظر

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel