Summary: HenCoder Android Development Advanced: Custom View 1-1 Drawing Basics

http://hencoder.com/ui-1-1/ , thank you very much for the sharing article of Parabola. This series mainly summarizes knowledge according to the author's reading habits. Students who like multiple pictures can move to the original address above.

//Divided into three parts: draw layout touch return
//Custom drawing: rewrite the drawing method, the most commonly used onDraw(). The use of the key Canvas for drawing: 1.drawXXX()2 range clipping, geometric transformation, covering
relationship
Configuration, set color, solid hollow, line thickness, whether there is shadow
//2. In-depth use of Paint: what shape does the corner need? Can you enable bilinear filtering? Add special effects?
//3. Canvas's assistance for drawing - range clipping and geometric transformation, which are not commonly used and can be used to do some cool effects.
//4. Use different drawing methods to control the drawing order: improve performance, realize by controlling the drawing order to make a View, not suitable for piecing together multiple or multi-layer Views

//The beginning of everything: onDraw()

Paint paint = new Paint();

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// 绘制一个圆
canvas.drawCircle(300, 300, 200, paint);

}

// The drawXXX() series of methods and the basis of Paint:
// All draw- header methods under the Canvas class, such as drawCircle() drawBitmap().
// Several of the most commonly used methods of the Paint class. Specifically:
// Paint.setStyle(Style style) Set the drawing mode
// Paint.setColor(int color) Set the color
// Paint.setStrokeWidth(float width) Set the line width
// Paint.setTextSize(float textSize) Set the text size
// Paint.setAntiAlias(boolean aa) sets the antialiasing switch

// Canvas.drawColor(@ColorInt int color) Color fill
// This is the most basic drawXXX() method: uniformly paint the specified color in the entire drawing area.
// For example, drawColor(Color.BLACK) will dye the entire area into pure black, covering the original content;
// drawColor(Color.parse(“#88880000”) will add a layer of translucent to the original drawing effect Red mask.
// Similar methods are drawRGB(int r, int g, int b) and drawARGB(int a, int r, int g, int b), they are just different from drawColor(color) It 's all the same.
// canvas.drawRGB(100, 200, 100);
// canvas.drawARGB(100, 100, 200, 100);
// This type of color filling method is generally used to set the background color before drawing, Or set a translucency mask for the interface after drawing.

// drawCircle(float centerX, float centerY, float radius, Paint paint) draw a circle
// The first two parameters centerX centerY are the coordinates of the center of the circle, and the third parameter radius is the radius of the circle, in pixels, which together constitute The basic information of this circle (that is, a definite circle can be constructed with these information);
// The fourth parameter paint provides all style information other than the basic information, such as color, hollow solid, line thickness, shadow, etc.
// Note: In Android, each View has its own coordinate system, which does not affect each other.
// The origin of this coordinate system is the point in the upper left corner of the View; the horizontal direction is the x-axis, the right is positive and the left is negative; the vertical direction is the y-axis, the bottom is positive and the top is negative
// ​​So the coordinates of a View are at (x, y) , which refers to a point that is x pixels in the horizontal direction and y pixels in the vertical direction relative to the point in the upper left corner of it. For example, (300, 300) means the upper left point is 300 to the right and 300 down; (100, -50) means the upper left point is 100 right and 50 up.

public information. For example, the color of the graphics, the hollow solid, etc., you may use it whether you are drawing a circle or a square. These information are uniformly placed in the paint parameter.
Paint.setColor(int color)
paint.setColor(Color.RED); // Set to red
canvas.drawCircle(300, 300, 200, paint);
Paint.setStyle(Paint.Style style)
There are three styles: FILL, STROKE and FILL_AND_STROKE. FILL is the fill mode, STROKE is the line drawing mode (that is, the outline mode), and FILL_AND_STROKE is the two modes used together: both line drawing and filling. Its default value is FILL, fill mode
paint.setStyle(Paint.Style.STROKE) to change the drawing mode to line drawing mode
canvas.drawCircle(300, 300, 200, paint);
Paint.setStrokeWidth(float width)
in STROKE and FILL_AND_STROKE, you can also use paint.setStrokeWidth(float width) to set the width of the line:

paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(20); // The line width is 20 pixels
canvas.drawCircle(300, 300, 200, paint);
When drawing, it is often necessary to turn on anti-aliasing to make The edges of graphics and text are smoother. Turning on anti-aliasing is very simple, just add an ANTI_ALIAS_FLAG parameter to new Paint():
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint.setAntiAlias(boolean aa) to dynamically switch anti-aliasing
because anti-aliasing is not necessarily suitable for all scenarios

The above is the local effect of enlarging the two circles in front. Do you see it? A circle with no antialiasing turned on, all pixels are the same black, while a circle with antialiasing turned on, the color of the edges is slightly changed. This change can give the eye a feeling of smooth edges, but in a way, it also causes the color distortion of the graphics.
So, is antialiasing any good? Well, most of the time it should be on; but on rare occasions you really need to turn it off. When is "sometimes"? You will know when you use it.

drawRect(float left, float top, float right, float bottom, Paint paint) Draw rectangle
left, top, right, bottom are the coordinates of the four sides of the rectangle.

paint.setStyle(Style.FILL);
canvas.drawRect(100, 100, 500, 500, paint);
paint.setStyle(Style.STROKE);
canvas.drawRect(700, 100, 1100, 500, paint); in
addition, It also has two overloaded methods, drawRect(RectF rect, Paint paint) and drawRect(Rect rect, Paint paint), which allow you to directly fill in a RectF or Rect object to draw a rectangle.

drawPoint(float x, float y, Paint paint) draw point
x and y are the coordinates of the point. The size of the point can be set by paint.setStrokeWidth(width); the shape of the point can be set by paint.setStrokeCap(cap): ROUND draws a circular point, SQUARE or BUTT draws a square point. (A point and a shape? Yes, that’s what Google said anyway. If you ask Google, I’m also very confused.)

Note: Paint.setStrokeCap(cap) can set the shape of the point, but this method is not specifically used to set the shape of the point, but a method to set the shape of the end point of the line. There are three types of endpoints: ROUND, BUTT, and SQUARE, which will be discussed in the next section.
paint.setStrokeWidth(20);
paint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawPoint(50, 50, paint);
paint.setStrokeWidth(20);
paint.setStrokeCap(Paint.Cap.SQUARE);
canvas.drawPoint (50, 50, paint); It
seems a bit like drawCircle() and drawRect() in FILL mode

    drawPoints(float[] pts, int offset, int count, Paint paint) / drawPoints(float[] pts, Paint paint) 画点(批量)
    同样是画点,它和 drawPoint() 的区别是可以画多个点。pts 这个数组是点的坐标,每两个成一对;offset 表示跳过数组的前几个数再开始记坐标;count 表示一共要绘制几个点。说这么多你可能越读越晕,你还是自己试试吧,这是个看着复杂用着简单的方法。

    float[] points = {0, 0, 50, 50, 50, 100, 100, 50, 100, 100, 150, 50, 150, 100};

// draw four points: (50, 50) (50, 100) (100, 50) (100, 100)
canvas.drawPoints(points, 2 /* skip two numbers, i.e. the first two 0s */,
8 /* Draw a total of 8 numbers (4 points)*/, paint);
}

drawOval(float left, float top, float right, float bottom, Paint paint) Draw ellipse
Only horizontal or vertical ellipse can be drawn, not oblique (oblique is also possible, but instead of using drawOval() directly, It is to cooperate with geometric transformation, which will be discussed later). left, top, right, bottom are the coordinates of the left, top, right, and bottom of the ellipse.

paint.setStyle(Style.FILL);
canvas.drawOval(50, 50, 350, 200, paint);
paint.setStyle(Style.STROKE);
canvas.drawOval(400, 50, 700, 200, paint); in
addition, It also has an overloaded method drawOval(RectF rect, Paint paint) that allows you to fill in RectF directly to draw an ellipse.

drawLine(float startX, float startY, float stopX, float stopY, Paint paint) draw line
startX, startY, stopX, stopY are the start and end coordinates of the line respectively.

canvas.drawLine(200, 200, 800, 500, paint);
Since the line is not a closed shape, setStyle(style) has no effect on the line.

drawLines(float[] pts, int offset, int count, Paint paint) / drawLines(float[] pts, Paint paint) Draw Lines (Batch)
drawLines() is the plural of drawLine().

float[] points = {20, 20, 120, 20, 70, 20, 70, 120, 20, 120, 120, 120, 150, 20, 250, 20, 150, 20, 150, 120, 250, 20, 250, 120, 150, 120, 250, 120};
canvas.drawLines(points, paint);

drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) draw a rounded rectangle
left, top, right, bottom are the coordinates of the four sides, rx and ry are the horizontal radius and the Vertical radius.

canvas.drawRoundRect(100, 100, 500, 300, 50, 50, paint);
In addition, it has an overloaded method drawRoundRect(RectF rect, float rx, float ry, Paint paint), which allows you to fill in RectF directly Draw a rounded rectangle.

drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) Drawing an arc or fan
drawArc() uses an ellipse to describe the arc. left, top, right, bottom describe the ellipse where the arc is located; startAngle is the starting angle of the arc (the positive direction of the x-axis, that is, the right direction, is the position of 0 degrees; clockwise is a positive angle, Counterclockwise is a negative angle), sweepAngle is the angle of the arc; useCenter indicates whether it is connected to the center of the circle, if it is not connected to the center of the circle, it is an arc, and if it is connected to the center of the circle, it is a fan.

paint.setStyle(Paint.Style.FILL); // fill mode
canvas.drawArc(200, 100, 800, 500, -110, 100, true, paint); // draw fan
canvas.drawArc(200, 100, 800 , 500, 20, 140, false, paint); // draw arc
paint.setStyle(Paint.Style.STROKE); // line drawing mode
canvas.drawArc(200, 100, 800, 500, 180, 60, false , paint); // draw an uncapped arc

So far, the above is the drawing of all simple graphics on Canvas. In addition to drawing simple graphics, Canvas can also use drawPath(Path path) to draw custom graphics.

drawPath(Path path, Paint paint) Drawing custom graphics
This method is a bit complicated and needs to be expanded.
The previous methods all draw a given graphic, and drawPath() can draw custom graphics.
When the graphics you want to draw are special and cannot be done using the previous methods, you can use drawPath() to draw them.
drawPath(path) This method draws graphics by describing the path, and its path parameter is the object used to describe the graphics path. The type of path is Path, and the usage method is probably as follows:
public class PathView extends View {

Paint paint = new Paint();
Path path = new Path(); // 初始化 Path 对象
......

{
    // 使用 path 对图形进行描述(这段描述代码不必看懂)
    path.addArc(200, 200, 400, 400, -225, 225);
    path.arcTo(400, 200, 600, 400, -180, 225, false);
    path.lineTo(400, 542);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(path, paint); // 绘制出 path 描述的图形(心形),大功告成
}

}

Path can describe lines, quadratic curves, cubic curves, circles, ellipses, arcs, rectangles, and rounded rectangles. Combining these graphics, many complex graphics can be described. Now I will talk about how to describe these graphs in detail.

Path has two types of methods, one is directly describing the path, and the other is auxiliary settings or calculations.

The first class of Path methods: directly describe the path.
This class of methods can also be subdivided into two groups: adding sprites and drawing lines (straight lines or curves)

The first group: addXxx() - add sub-graphics
addCircle(float x, float y, float radius, Direction dir) Add circle
x, y, radius These three parameters are the basic information of the circle, and the last parameter dir is to draw a circle the direction of the path.

There are two path directions: clockwise (CW clockwise) and counter-clockwise (CCW counter-clockwise). For normal cases, it doesn't matter whether this parameter is filled with CW or CCW.
It is only used to judge the filling range when the graphics need to be filled (Paint.Style is FILL or FILL_AND_STROKE) and the graphics appear self-intersection. For example the following graph:

It's up to you to decide which way you want to fill it. Specifically how to do it, I will introduce it in detail when I talk about Path.setFillType(), but here you can ignore the dir parameter first.

After adding a circle to Path with addCircle(), call canvas.drawPath(path, paint) to draw a circle. like this:

path.addCircle(300, 300, 200, Path.Direction.CW);

canvas.drawPath(path, paint);
It can be seen that path.AddCircle(x, y, radius, dir) + canvas.drawPath(path, paint) is written, and canvas.drawCircle(x, y, radius is used directly , paint) The effect is the same, the
difference is that its writing is more complicated. So if you only draw a circle, you don't need to use Path, just use drawCircle() directly. drawPath() is generally used when drawing combined graphics.

Other Path.add-() methods are similar, for example:

addOval(float left, float top, float right, float bottom, Direction dir) / addOval(RectF oval, Direction dir) add ellipse
addRect(float left, float top, float right, float bottom, Direction dir) / addRect(RectF rect , Direction dir) add rectangle
addRoundRect(RectF rect, float rx, float ry, Direction dir) / addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Direction dir) / addRoundRect(RectF rect, float[] radii, Direction dir) / addRoundRect(float left, float top, float right, float bottom, float[] radii, Direction dir) Add a rounded rectangle
addPath(Path path) Add another Path
The above methods and The use of addCircle() is similar, so I won't introduce it too much.

The second group: xxxTo() - the difference between this group of drawing lines (straight lines or curves)
and the first group of addXxx() methods is that the first group is a complete closed graph added (except addPath() ), and this one All the group adds is a line.

lineTo(float x, float y) / rLineTo(float x, float y) Draw a line Draw a line
from the current position to the target position, where x and y are the coordinates of the target position. The difference between these two methods is that the parameters of lineTo(x, y) are absolute coordinates, while the parameters of rLineTo(x, y) are relative coordinates relative to the current position (the prefix r refers to "relatively").

Current position: The so-called current position, that is, the end position of the last call to draw the Path method. The initial value is the origin (0, 0).

paint.setStyle(Style.STROKE);
path.lineTo(100, 100); // draw a straight line from the current position (0, 0) to (100, 100)
path.rLineTo(100, 0); // from the current position position(100, 100) draws a line 100 pixels to the right

quadTo(float x1, float y1, float x2, float y2) / rQuadTo(float dx1, float dy1, float dx2, float dy2) Draw a quadratic
Bezier curve The starting point of this quadratic Bezier curve is the current position. The parameters x1, y1 and x2, y2 are the coordinates of the control point and the end point, respectively. Similar to rLineTo(x, y), the parameters of rQuadTo(dx1, dy1, dx2, dy2) are also relative coordinates

Bezier Curve: A Bezier curve is a type of curve in geometry. It describes a curve by starting point, control point and end point and is mainly used in computer graphics.
The concept is always easy to say and difficult to listen to. In short, it can be used to draw many round and beautiful graphics, but it is not easy to master and use it flexibly.
But the good thing is that in general, the Bezier curve is not very useful, it is only used when drawing some special graphics in a few scenarios, so if you haven't mastered custom drawing,
you can first use the Bezier curve It's perfectly fine to let go of the Er curve and learn it later. As for how to learn, I have searched a lot about the knowledge of Bezier curves on the Internet, so I will not talk about it here.

cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) / rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3) draw cubic bezier curve
and the above QuadTo() The quadratic Bezier curve of rQuadTo() is the same, cubicTo() and rCubicTo() are cubic Bezier curves and will not be explained.

moveTo(float x, float y) / rMoveTo(float x, float y) Move to the target position
Whether it is a straight line or a Bezier curve, the current position is used as the starting point, and the starting point cannot be specified. But you can indirectly set the starting point of these methods by changing the current position with moveTo(x, y) or rMoveTo().

paint.setStyle(Style.STROKE);
path.lineTo(100, 100); // draw slash
path.moveTo(200, 100); // i move~~
path.lineTo(200, 0); // draw vertical bar

moveTo(x, y) does not add graphics, but it sets the starting point of the graphics, so it is a very important helper method.

arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
/ arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo)
/ arcTo(RectF oval, float startAngle, float sweepAngle) Compared with Canvas.drawArc(),
this method has one less parameter useCenter and one more parameter forceMoveTo.

The reason that useCenter is missing is because arcTo() is only used to draw arcs and not sectors, so the useCenter parameter is no longer needed;
and the extra forceMoveTo parameter means that the drawing is to "lift the pen to move over", or "Drag the pen directly", the difference is whether to leave traces of movement.

paint.setStyle(Style.STROKE);
path.lineTo(100, 100);
path.arcTo(100, 100, 300, 300, -90, 90, true); // Force move to arc start point (no trace)

paint.setStyle(Style.STROKE);
path.lineTo(100, 100);
path.arcTo(100, 100, 300, 300, -90, 90, false); // directly connect to the arc starting point (with trace)

addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle)
/ addArc(RectF oval, float startAngle, float sweepAngle)
is another arc method. One is called arcTo and the other is called addArc(). Both are arcs. What is the difference? It's actually quite simple: addArc() is just a simplified version of arcTo() that uses forceMoveTo = true directly.

paint.setStyle(Style.STROKE);
path.lineTo(100, 100);
path.addArc(100, 100, 300, 300, -90, 90);

"Sub-graphics": It is called contour in the official documentation. But since I couldn't find a proper Chinese translation of this word in this scenario (the literal translation is called "contour"),
I changed it to a word that is easier for Chinese people to understand: "sub-figure". As mentioned earlier, the first group of methods is "adding sub-graphics". The so-called "sub-graphics" refers to an uninterrupted connection.
A Path can contain multiple subgraphics. When using the first group of methods, namely addCircle() addRect() and other methods, each method call adds an independent sub-graphic;
and if using the second group of methods, namely lineTo() arcTo() etc. When the method is used, each disconnection (that is, each "pen lift") marks the end of a sub-graphic and the beginning of a new sub-graphic.

Also, not all sprites need to be closed with close(). When the graphics need to be filled (ie Paint.Style is FILL or FILL_AND_STROKE), Path will automatically close the sub graphics.

java
paint.setStyle(Style.FILL);
path.moveTo(100, 100);
path.lineTo(200, 100);
path.lineTo(150, 150);
// only two sides are drawn here, but due to Style is FILL, so it will be automatically capped when drawing

The above is the first method of Path: directly describing the path.

The second type of Path method: auxiliary setting or
calculation There are few usage scenarios for this type of method, so I won't talk about it here, only one of the methods: setFillType(FillType fillType).

Path.setFillType(Path.FillType ft) Set the filling method As mentioned
earlier when talking about the dir parameter, Path.setFillType(fillType) is used to set the filling algorithm when the graph is self-intersecting:
fill in different FillType values ​​in the method, There will be different filling effects. FillType has four values:

EVEN_ODD WINDING (default value) NVERSE_EVEN_ODD INVERSE_WINDING
The last two are prefixed with INVERSE_, but
the principle of EVEN_ODD and WINDING is a bit complicated, so I will give a simple one first. Summary of the rough version: WINDING is "full fill", and EVEN_ODD is "cross fill":
the reason for the "simple and rough version" is because these are only the effects of the general situation; and if you want to know exactly how they work in all cases To achieve the effect, you must first know their principle, that is, their specific algorithm.

Well, it took a long time to talk about drawPath(path) and Path, and finally finished. At the same time, the drawing of graphics by Canvas is finished.
When the graphics are simple, use methods such as drawCircle() and drawRect() to draw directly; when the graphics are complex, use drawPath() to draw custom graphics.

In addition, Canvas can also draw Bitmap and text.

drawBitmap(Bitmap bitmap, float left, float top, Paint paint) Draw Bitmap
to draw Bitmap object, that is, paste the pixel content in this Bitmap. Where left and top are the coordinates of the position where the bitmap should be drawn. Its use is very simple.
Its overloaded method:

drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
/ drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
/ drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)

drawBitmap also has a sibling method drawBitmapMesh(), which can draw a Bitmap with mesh stretching effect. drawBitmapMesh() has few usage scenarios, so I won't talk about it. If you are interested, you can study it yourself.

drawText(String text, float x, float y, Paint paint) Draw text All display contents in the
interface are drawn, including text. drawText() This method is used to draw text. The parameter text is the string used to draw, and x and y are the coordinates of the starting point for drawing.

canvas.drawText(text, 200, 100, paint);
Note: Paint.setTextSize(float textSize)
can set the size of the text through Paint.setTextSize(textSize).

paint.setTextSize(18);
canvas.drawText(text, 100, 25, paint);
paint.setTextSize(36);
canvas.drawText(text, 100, 70, paint);
paint.setTextSize(60);
canvas.drawText (text, 100, 145, paint);
paint.setTextSize(84);
canvas.drawText(text, 100, 240, paint);
Set the position and size of the text, these are just the most basic operations for drawing text.
The drawing of text is highly customizable, but because its customization is too high, I will use a special issue later to talk about the drawing of text. Not much to say in this episode.

Hmm...that's it. The first section of the drawing part, the drawXXX() series methods of Canvas and the basic use of Paint, is here.

The next issue is a comprehensive introduction to Paint. The content will be more difficult than this issue, but it will also be more interesting. Get ready.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325901597&siteId=291194637