Author’s Microblog:@GcsSloop

【 Related articles in this series 】

In the last article custom View classification and process we understand the basic knowledge of custom View, but these things are still theoretical, and can not be used (B), this time we understand some can (zhaung) with (B) things.

In this article, we’ll look at the basics of Canvas usage and conclude with a small example.

A. Introduction of Canvas

Canvas, we can call it Canvas, can draw all kinds of things on it, is the basis of 2D graphics drawing on Android platform, very powerful.

In general, there are two main characteristics of relatively basic things:

1. Maneuverability: Since these are the foundation of the upper layer, maneuverability must be very strong.

2. Difficult to use: The methods are so basic that it is difficult to combine them perfectly.

But don’t worry, this series of articles will cover not only how to operate the Canvas, but also some design ideas and techniques.

Ii. Quick reference table of common operations of Canvas

Operation type The relevant API note
Paint color drawColor, drawRGB, drawARGB Fill the entire canvas with a single color
Draw basic shapes drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc Point, line, rectangle, rounded rectangle, ellipse, circle, arc
Draw pictures drawBitmap, drawPicture Draw bitmaps and pictures
Draw text drawText, drawPosText, drawTextOnPath Draw text, specify the position of each text when drawing text, and draw text according to the path
Draw the path drawPath It’s also used when you plot a path, when you plot bezier curves
Vertex operations drawVertices, drawBitmapMesh We can deform images by manipulating vertices. The drawVertices are used directly on the canvas, and the drawBitmapMesh is used only on the drawn Bitmap
The canvas clipping clipPath, clipRect Sets the display area of the canvas
The canvas snapshot save, restore, saveLayerXxx, restoreToCount, getSaveCount Save the current state, roll back to the last saved state, save the layer state, roll to the specified state, and obtain the save times
The canvas transform translate, scale, rotate, skew The order is displacement, scaling, rotation, tilt
Matrix (Matrix) getMatrix, setMatrix, concat The actual canvas displacement, scaling and other operations are all image Matrix, but Matrix is difficult to understand and use, so it encapsulates some commonly used methods.

PS: The common methods of Canvas are all listed in the table above. Of course, there are some other methods that are not listed. For details, please refer to the official document Canvas

3. Canvas explanation

This article focuses on how to use Canvas to draw basic graphics.

Draw color:

Paint color fills the entire canvas and is often used to paint a background color.

canvas.drawColor(Color.BLUE); // Draw blue

For more information on colors, see basics _ Colors

Create a brush:

To draw content, first create a brush like this:

Private Paint mPaint = new Paint(); Private void initPaint() {mPaint. SetColor (color.black); // Set the Paint color mPaint. SetStyle (paint.style.fill); // Set the brush mode to fill mPaint. SetStrokeWidth (10f); Public SloopView(Context Context, AttributeSet attrs) {super(Context, attrs); initPaint(); }

Once you’ve created the brush, you can draw various things on the Canvas.

Draw a point:

You can draw a point or a group of points as follows:

canvas.drawPoint(200, 200, mPaint); DrawPoints (new float[]{// Draw a group of points, 500,500, 500,600, 500,700},mPaint);

By default, the origin of coordinates is in the upper left corner. Horizontally to the right is the increasing direction of x axis, vertically downward is the increasing direction of Y axis.

See the basic _ coordinate system here for more information

Draw a line:

Drawing a line requires two points, the starting point and the ending point. Similarly, drawing a line can also draw one or a group of points:

Canvas. DrawLine (300300500600, mPaint); DrawLines (new float[]{// draw a group of lines every four digits (the coordinates of two points) to determine the line 100,200,200,200, 200,200, 100300200300}, mPaint);

Draw rectangle:

To determine a rectangle, at least four data points are required, namely the coordinates of the two points on the diagonal. Usually, we use the coordinates of the two points on the upper left and lower right (of course, the upper right and lower left can also be used).

Canvas provides three overloaded methods for drawing a rectangle. The first method is to provide four values (coordinates of two points on the diagonal) to determine a rectangle for drawing. The other two are to first encapsulate the rectangle as Rect or RectF(actually it is still a rectangle determined by two coordinate points) and then pass it to Canvas for drawing, as follows:

DrawRect (100,100,800,400,mPaint); Rect = new Rect(100,100,800,400); Rect = new Rect(100,100,800,400); canvas.drawRect(rect,mPaint); RectF = new RectF(100,100,800,400); RectF = new RectF(100,100,800,400); canvas.drawRect(rectF,mPaint);

The results drawn by the above three methods are identical.

Seeing this, I believe many viewers will have a question: why are there two kinds of Rect and RectF? What’s the difference?

Rect is an int. RectF is a float. RectF is a float. In addition to the difference in accuracy, there are slight differences between the two methods provided. We don’t need to pay attention here for the moment. For more information, please refer to the official documents Rect and RectF

Draw rounded rectangle:

Drawing rounded rectangles also provides two ways to reload, as follows:

RectF = new RectF(100,100,800,400); RectF = new RectF(100,100,800,400); Canvas. DrawRoundRect (rectF, 30, 30, mPaint); / / the second canvas. DrawRoundRect (100100800400,30,30, mPaint);

The above two methods have the same drawing effect, but since the second method was added in API21, we generally use the first method.

Here’s a quick parse of the necessary parameters for rounded rectangles.

Paint (rx, ry); Paint (rx, ry); Paint (rx, ry); Paint (rx, ry)

A little analysis, since it is a rounded rectangle, its angles must be an arc (part of a circle), what do we usually use to determine a circle?

The answer is the center of the circle, used to determine position, and the radius, used to determine size.

Since the position of the rectangle is determined, so are the positions of its edges and corners, the parameters for determining the position can be omitted, and only the radius can be used to describe an arc.

But the radius only takes one parameter, but how can there be two here?

Well, here you see, the Angle of the rounded rectangle is not actually a perfectly circular arc, but an ellipse. The two parameters here are actually two radii of the ellipse, and they look like this:

The red line rx and ry are the two radii, the two extra parameters compared to the drawing rectangle.

Now that we know how it works, we can do whatever we want, and we can calculate that the rectangle we drew last time was 700 wide and 300 high, and when you make rx greater than 350(half the width) and RY greater than 150(half the height) the magic happens, and you find that the rounded rectangle becomes an ellipse, They draw like this (I changed the brush color to make sure I drew the rectangle and rounded rectangle at the same time) :

RectF = new RectF(100,100,800,400); // Draw the background rectangle mPaint. SetColor (color.gray); canvas.drawRect(rectF,mPaint); // Draw the rounded rectangle mPaint. SetColor (color.blue); Canvas. DrawRoundRect (rectF, 700400, mPaint);

The gray part is our selected rectangle, and inside the rounded rectangle is turned into an oval, in fact, half the width of rx for ry for the height of the half hour, is just an oval, through the analysis above we figure out the principle of the can get, and when the rx is greater than the width of the half, ry is greater than the height of the half hour, is actually unable to calculate the circular arc, So drawRoundRect limits (corrects) the number of arguments that are greater than this value, and treats any argument that is greater than half as half.

Draw an ellipse:

Drawing an ellipse is much easier than drawing a rounded rectangle, because it only needs a rectangle as a parameter:

RectF = new RectF(100,100,800,400); RectF = new RectF(100,100,800,400); canvas.drawOval(rectF,mPaint); // canvas. DrawOval (100,100,800,400,mPaint);

Again, the above two methods have exactly the same effect, but the first method is generally used.

Drawing an ellipse is actually drawing the tangent shape of a rectangle, which works as follows without further elaboration:

PS: If you pass in a rectangle of equal length and width (i.e. a square), then the drawing is actually a circle.

Draw round –

Drawing circles is also easier, as follows:

Canvas. Methods like drawCircle (500500400, mPaint); // Draw a circle with a center of (500,500) and a radius of 400.Copy the code

Drawing a circle takes four parameters, the first two are the center coordinates, the third is the radius, and the last is the brush.

Draw an arc:

Drawing arcs is a little bit more magical. To understand this magical thing, let’s take a look at the parameters it takes:

Public void drawArc(@nonnull RectF oval, float startAngle, float sweepAngle, Booleusecenter, @nonnull Paint Paint){} public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint) {}

As can be seen from the above, there are three more parameters to draw an arc than an ellipse:

StartAngle // startAngle sweepAngle // sweepAngle useCenter // whether to useCenter

The first two parameters (startAngle, sweepAngel) are used to determine the starting position and sweep Angle, but the third parameter is used for what? Give it a try and find out.

RectF RectF = new RectF(100,100,800,400); // Draw the background rectangle mPaint. SetColor (color.gray); canvas.drawRect(rectF,mPaint); // Draw the arc mPaint. SetColor (color.blue); Canvas. DrawArc (rectF, 0, living, false, mPaint); / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- RectF rectF2 = new RectF (100600800900); // Draw the background rectangle mPaint. SetColor (color.gray); canvas.drawRect(rectF2,mPaint); // Draw the arc mPaint. SetColor (color.blue); Canvas. DrawArc (rectF2, 0, living, true, mPaint);Copy the code

The above code actually draws an arc that starts at 0 degrees and sweeps through 90 degrees. The difference between the two is whether the center point is used. The result is as follows:

It can be found that after using the center point, the graph is similar to a sector, while without using the center point, it is the line between the starting and ending points of the arc and the graph surrounded by the arc. So it’s pretty obvious what the center point is doing, and I don’t have to say anything about it. In addition, you can refer to this article about Angle: Angle and radian

Instead of using ellipses, we use perfect circles to show the effect:

RectF RectF = new RectF(100,100,800,400); // Draw the background rectangle mPaint. SetColor (color.gray); canvas.drawRect(rectF,mPaint); // Draw the arc mPaint. SetColor (color.blue); Canvas. DrawArc (rectF, 0, living, false, mPaint); / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- RectF rectF2 = new RectF (100600800900); // Draw the background rectangle mPaint. SetColor (color.gray); canvas.drawRect(rectF2,mPaint); // Draw the arc mPaint. SetColor (color.blue); Canvas. DrawArc (rectF2, 0, living, true, mPaint);Copy the code

Paint

After reading so much above, I believe some people will have a question, what if I want to draw a circle, as long as the edges do not have the color inside?

Very simple, the basic shape of the drawing is determined by the Canvas, but the color and effect of the drawing are determined by the Paint.

If you noticed, at the beginning we set the brush style like this:

mPaint.setStyle(Paint.Style.FILL); // Set the brush mode to Fill

In order to facilitate the display and see the effect, the mode used before has always been the fill mode. In fact, there are three modes of brush, as follows:

STROKE FILL // FILL FILL_AND_STROKE // STROKE FILL

In order to distinguish the three effects, we did the following experiments:

Paint paint = new Paint(); paint.setColor(Color.BLUE); paint.setStrokeWidth(40); // Paint. SetStyle (paint. Style.stroke); Canvas. Methods like drawCircle (200200100, paint); / / FILL paint. SetStyle (paint. Style. The FILL); Canvas. Methods like drawCircle (200500100, paint); SetStyle (paint.style.fill_and_stroke); canvas.drawCircle(200, 800, 100, paint);Copy the code

A picture is worth a thousand words. Through the above experiments, we can clearly see the difference between the three modes. If only the edge is needed and no content is needed to be filled, the mode can be set as STROKE.

There are quite a few things about Paint, but this is just the tip of the iceberg, and we’ll cover Paint in more detail in a future post.

The small sample

A brief introduction to canvas operations:

The details of the canvas manipulation will be covered in the next article and are not the focus of this article, but may be used in the following examples, so I’ll cover them briefly here.

The relevant operation A brief introduction to
save Saves the current canvas state
restore Rollback to the last saved state
translate Displacement relative to current position
rotate rotating

Make a pie chart

Pie charts are often used when presenting percentage data, like this:

Simple analysis

We can actually make our own pie chart based on what we know above. But the most important thing about making something is not the result, but the idea. Trust me, I’m going to paste the code and you’ll see it immediately. It’s very simple. However, we still want to understand the idea of production:

If you look at the composition of the pie chart, it is obvious that the pie chart is made up of one pie pie after another, each pie pie has a different color, corresponding to the name, data and percentage.

From the above information, it can be concluded that the most basic data of the pie chart should include: name, data, value, percentage, corresponding Angle and color.

Data users care about: percentage of name data values

Data to be calculated by the program: the Angle corresponding to the percentage

Among them, the color can be specified by the user or the program (we use the program to specify).

Encapsulating data:

Public class PieData {private String name; // name private float value; // Private float percentage; Private int color = 0; private int color = 0; Private float Angle = 0; Public PieData(@nonNULL String name, @nonNULL float Value) {this.name = name; this.value = value; }}

PS: The get set method is omitted above

Custom View:

First, follow the custom View process to comb through (determine what each step should do) :

steps The keyword role
1 The constructor Initialize (initialize the Paint)
2 onMeasure Measure the size of the View (don’t worry about it for now)
3 onSizeChanged Size the View (record the width and height of the current View)
4 onLayout Determine the child View layout (no child View, don’t care)
5 onDraw Actual drawing (pie chart drawing)
6 Provide the interface Provide interface (provide interface for setting data)

The code is as follows:

Public class PieView extends View {private int[] mColors = {0xFFCCFF00, 0xFF6495ED, 0xFFE32636, 0xFF800000, 0xFF808000, 0xFFFF8C69, 0xFF808080, 0xFFE6B800, 0xFF7CFC00}; Private float mStartAngle = 0; Private ArrayList mData; Private int mWidth, mHeight; Private Paint mPaint = new Paint(); public PieView(Context context) { this(context, null); } public PieView(Context context, AttributeSet attrs) { super(context, attrs); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (null == mData) return; float currentStartAngle = mStartAngle; Canvas. Translate (mWidth / 2, mHeight / 2); Float r = (float) (math.min (mWidth, mHeight) / 2 * 0.8); RectF rect = new RectF(-r, -r, r, r); For (int I = 0; i < mData.size(); i++) { PieData pie = mData.get(i); mPaint.setColor(pie.getColor()); canvas.drawArc(rect, currentStartAngle, pie.getAngle(), true, mPaint); currentStartAngle += pie.getAngle(); Public void setStartAngle(int mStartAngle) {this.mStartAngle = mStartAngle; invalidate(); Public void setData(ArrayList mData) {this.mdata = mData; initDate(mData); invalidate(); / / refresh} / / initialized data private void initDate (ArrayList mData) {if (null = = mData | | mData. The size () = = 0) / / data has a problem Direct return to return; float sumValue = 0; for (int i = 0; i < mData.size(); i++) { PieData pie = mData.get(i); sumValue += pie.getValue(); Int j = I % McOlor.length; // set pie.setColor(mColors[j]); } float sumAngle = 0; for (int i = 0; i < mData.size(); i++) { PieData pie = mData.get(i); float percentage = pie.getValue() / sumValue; Float Angle = percentage * 360; float Angle = percentage * 360; Pie.setpercentage (percentage); // Record the percentage pie.setangle (Angle); SumAngle += Angle; Log.i("angle", "" + pie.getAngle()); }}}

PS: invalidate() is called when the interface is redrawn due to data changes.

rendering

PS: This pie chart does not add data such as percentages, but is used as an example.

Conclusion:

In fact, as long as the custom View according to the process step by step, is also relatively easy. But there are also a lot of pits, these pits or stepped on their own impression is deep, suggest that you do not directly copy source code, their own hand play experience.

About Me

Author’s Microblog:@GcsSloop

References:

View Canvas Android Canvas drawing details