View Customization – Path Path

Reference article

1. Concept

Used to describe the sequence & amp; area, single use has no effect.

The essence of graphics drawing is to draw points first and then connect the points, so there is a sequence between points. The direction of the graphics affects: determining the closing order when adding graphics (the recording order of each point), and the rendering results of the graphics (an important condition for judging graphics rendering).

1.1 Open path, closed path

1.2 Determine whether the point is inside the graph or outside the graph

1.2.1 Odd-even rules

Draw a ray from any position P and determine the number of points that intersect with the edges of the graph:

  • If the number of intersecting points is an odd number, P is considered to be a point within the graph.
  • If the number of intersecting points is an even number, P is considered to be a point outside the graph.

1.2.2 Non-zero wraparound number rules

Draw a ray from any position P. When point P moves along the direction of the ray, determine the edges that pass through the ray in each direction:

  • +1 whenever an edge of the shape crosses the ray from right to left, and -1 when crossing it from left to right.
  • If the number of wraps is ≠0, then P is a point inside the graph, otherwise it is a point outside the graph.

2. Create objects

The starting point coordinate of Path defaults to (0,0).

  • Create a global Path object and modify it as needed in onDraw(). Do not create it in onDraw(), because if the View refreshes frequently, objects will be created frequently, slowing down the refresh speed.
val path = Path()

3. Set path

moveTo()

public void moveTo(float x, float y)

Move to a new coordinate point and use it as a new starting point, affecting subsequent path drawing.

setLastPoint()

public void setLastPoint(float dx, float dy)

Moving to a new coordinate point will not change the original starting point and will not affect subsequent path drawing.

lineTo()

public void lineTo(float x, float y)

Move to the new coordinate point and connect it with the previous point with a straight line.

close()

public void close()

Close the path and connect the current point to the starting point. If the end point and start point cannot be connected to form a closed shape, nothing will be done.

First lineTo(400,500), use moveTo(300,300) and setLastPoint(300,300) respectively, then lineTo(900,800), and finally close().

4. Reset path

FillType affects the display effect, and the data structure affects the reconstruction speed. Generally, reset() is selected.

reset()

public void reset()

The FillType setting is retained and the original data structure is not retained.

rewind()

public void rewind()

The FillType setting is not retained and the original data structure is retained.

5. Add path

5.1 Basic Graphics

Adding a graphic path will change the starting point of the path. The formal parameter dir is the direction, which specifies whether to draw clockwise or counterclockwise (CW is clockwise, CCW is counter-clockwise).

addRect()

rectangle

addRect(RectF rect, Path.Direction dir)

The path start point becomes the upper left vertex of the rectangle.

addRoundRect()

Rounded Rectangle

addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)

addArc()

arcTo()

Arc

public void addArc (RectF oval, float startAngle, float sweepAngle)

startAngle starting angle, sweepAngle draws the swept angle.

public void arcTo (RectF oval, float startAngle, float sweepAngle)

The only difference from the above method is: if the starting point of the arc and the last coordinate point are not the same, connect the two points.

public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)

forceMoveTo: Whether to set the end point of the previous path as the starting point of the arc. Set to true to draw an arc at the new starting point without connecting the last point to the starting point of the arc, that is, there is no intersection with the previous path (same as the first one). Set to false to draw an arc at the new starting point, but it will connect the end point of the previous path and the starting point of the arc, that is, it will intersect with the previous path (same as the second one).

addCircle()

round

addCircle(float x, float y, float radius, Path.Direction dir)

The starting point of the path becomes the largest point of the circle in the positive direction of the X-axis.

addOval()

Oval

addOval(RectF oval, Path.Direction dir)

oval as the circumscribed rectangular area of the ellipse.

canvas.translate(350, 500) //To facilitate observation, translate the coordinate system
path.addRect(0, 0, 400, 400, Path.Direction.CW) //clockwise
//path.addRect(0,0,400,400, Path.Direction.CCW) //Counterclockwise
canvas.drawPath(path paint)

5.1.1 Rectangle addRect()

addRect(RectF rect, Path.Direction dir)

The path start point becomes the upper left vertex of the rectangle.

5.1.2 Rounded rectangle addRoundRect()

addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)

5.1.3 Arc addArc(), arcTo()

public void addArc (RectF oval, float startAngle, float sweepAngle)

startAngle starting angle, sweepAngle draws the swept angle.

public void arcTo (RectF oval, float startAngle, float sweepAngle)

The only difference from the above method is: if the starting point of the arc and the last coordinate point are not the same, connect the two points.

public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)

forceMoveTo: Whether to set the end point of the previous path as the starting point of the arc. Set to true to draw an arc at the new starting point without connecting the last point to the starting point of the arc, that is, there is no intersection with the previous path (same as the first one). Set to false to draw an arc at the new starting point, but it will connect the end point of the previous path and the starting point of the arc, that is, it will intersect with the previous path (same as the second one).

5.1.4 Circle addCircle()

addCircle(float x, float y, float radius, Path.Direction dir)

The starting point of the path becomes the largest point of the circle in the positive direction of the X-axis.

5.1.5 Oval addOval()

addOval(RectF oval, Path.Direction dir)

oval as the circumscribed rectangular area of the ellipse.

5.2 Merging paths addPath()

public void addPath(Path src)
public void addPath(Path src, Matrix matrix)
public void addPath(Path src, float dx, float dy)

Add the src path, dx dy is offset first and then added, matrix is transformed first and then added.

canvas.translate(350, 500) //To facilitate observation, translate the coordinate system
Path pathRect = new Path()
Path pathCircle = new Path()
pathRect.addRect(-200, -200, 200, 200, Path.Direction.CW) // Draw a rectangular path
pathCircle.addCircle(0, 0, 100, Path.Direction.CW) //Draw a circular path
pathRect.addPath(pathCircle, 0, 200) //Move the circular path (0,200) and add it to the rectangular path
canvas.drawPath(pathRect,mPaint1) // Draw the merged path

6. Determine path attributes

6.1 Whether the content is empty isEmpty()

public boolean isEmpty()

Determine whether path contains content.

6.2 Whether it is a rectangle isRect()

public boolean isRect(RectF rect)

Determine whether the path is a rectangle. If so, the rectangle information will be stored in the square parameter rect.

path.lineTo(0,400)
path.lineTo(400,400)
path.lineTo(400,0)
path.lineTo(0,0)
RectF rect = new RectF()
boolean b = path.isRect(rect) //true
//rect stores information:
//rect.left = 0
//rect.top = 0
//rect.right = 400
//rect.bottom = 400

6.3 Replace set()

public void set(Path src)

Replace existing paths with src paths.

path1.addRect(-200,-200,200,200, Path.Direction.CW) //Rectangular path
path2.addCircle(0,0,100, Path.Direction.CW) //Circular path
path1.set(path2) //Replace the circular path with the rectangular path

6.4 Offset offset()

public void offset(float dx, float dy)

public void offset(float dx, float dy, Path dst)

Offset position, dst is used to store the translated path status but does not affect the current path.

canvas.translate(350, 500) //To facilitate observation, translate the coordinate system
//Add a circle to path (the center of the circle is at the origin of the coordinates)
path = new Path()
path.addCircle(0, 0, 100, Path.Direction.CW)
//Translate the path and store the translated state
Path dst = new Path()
path.offset(400, 0, dst) //Translation
canvas.drawPath(path, mPaint1) //Draw path
//Draw the translated graphics through dst (red)
mPaint1.setColor(Color.RED)
canvas.drawPath(dst,mPaint1)

7. Set path fill color xxxFillType()

getFillType()

public FillType getFillType()

Set fill rules.

setFillType()

public void setFillType(FillType ft)

Get the current filling rules.

isInverseFillType()

public boolean isInverseFillType()

Determine whether it is an INVERSE rule.

toggleInverseFillType()

public void toggleInverseFillType()

Switch filling rules (that is, switch between original rules and reverse rules).

  • Odd-even rule: EVEN_ODD
  • Anti-parity rule: INVERSE_EVEN_ODD
  • Non-zero wrapping number rules: WINDING
  • Inverse non-zero winding number rule: INVERSE_WINDING

canvas.translate(350, 500) //To facilitate observation, translate the coordinate system
path.addRect(-200, -200, 200, 200, Path.Direction.CW) // Add a rectangle to Path
path.setFillType(Path.FillType.EVEN_ODD) //Set the Path filling mode to odd-even rule
// path.setFillType(Path.FillType.INVERSE_EVEN_ODD) // Anti-parity rule
canvas.drawPath(path, mPaint1) //Draw the path

8. Boolean operation op()

The operation between two Paths uses simple graphics to synthesize relatively complex graphics through specific rules.

public boolean op(Path path, Op op)

Perform a Boolean operation on the calling path and the passed-in path, and the operation result is stored in the path that called the method.

public boolean op(Path path1, Path path2, Op op)

Perform a Boolean operation on path1 and path2, and store the operation result in the path that calls this method.

canvas.translate(550, 550) //To facilitate observation, translate the coordinate system
//Draw two circles
path1.addCircle(0, 0, 100, Path.Direction.CW)
path2.addCircle(50, 0,100, Path.Direction.CW)
//Get the XOR set of two paths
path1.op(path2, Path.Op.XOR)
//Draw the path
canvas.drawPath(path1, mPaint1)

9. Bezier Curve

Reference article

Mathematical formula for calculating curves. Any curve can be represented by a Bezier curve.

Data points Refers to the starting and ending points of the path.
Control points determine the curved trajectory of the path. According to the number of control points, Bezier curves are divided into first-order Bezier curves (0 control points), second-order Bezier curves (1 control point), and third-order Bezier curves (2 control points). point) and so on. n + 1st order Bezier curve = has n control points. (First order = a straight line, high-order can be broken down into multiple low-order curves)
Draw a second-order Bezier curve

public void quadTo(float x1, float y1, float x2, float y2)

(x1,y1) is the control point, (x2,y2) is the end point

public void rQuadTo(float dx1, float dy1, float dx2, float dy2)

(x1,y1) is the offset of the control point from the starting point, (x2,y2) is the offset of the end point from the starting point

Draw a third-order Bezier curve

public void conicTo(float x1, float y1, float x2, float y2, float weight)

(x1,y1), (x2,y2) are control points, (x3,y3) is the end point

public void rConicTo(float dx1, float dy1, float dx2, float dy2, float weight)

(x1,y1), (x2,y2) are the offsets of the control point from the starting point, (x3,y3) are the offsets of the end point from the starting point