Zero, preface,

1. Canvas itself provides many methods of drawing basic graphics, and ordinary drawing is basically satisfied 2. The more advanced canvas is helpless, but it has a method that connects the drawing of the graph to another dimension 3. DrawPath (Path,Paint) : drawPath(Path,Paint) This article will test all of Path’s apis.


1, lead: know Path

Example 1. Draw the grid

In the Canvas post, I used Path to draw a grid assist. MoveTo means to move the pen to a certain point, and lineTo means to draw down to a certain point

@param winSize @param winSize @param winSize */ public static path gridPath(int step, int step, int step, int step, int step) Point winSize) {// create path path path = new path (); // At each step, move the pen point to (0, step * I) and then draw the line to (winsize.x, step * I)for (int i = 0; i < winSize.y / step + 1; i++) {
            path.moveTo(0, step * i);
            path.lineTo(winSize.x, step * i);
        }

        for (int i = 0; i < winSize.x / step + 1; i++) {
            path.moveTo(step * i, 0);
            path.lineTo(step * i, winSize.y);
        }
        return path;
    }
Copy the code
MRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mRedPaint.setColor(Color.RED); mRedPaint.setStrokeWidth(2); mRedPaint.setStyle(Paint.Style.STROKE); // Set the dashed line effect to newfloat[] mredpaint.setpatheffect (new DashPathEffect(new)float[] {10, 5}, 0)); Path = HelpPath. GridPath (50, mW)inSize);
canvas.drawPath(path, mRedPaint);
Copy the code


Example 2. Draw n-angle stars

I once spent half a day studying the structure of five-pointed star, found the general method of drawing N-pointed star through two circles, and realized drawing in the browser with JavaScript Canvas for half a day, of course Android is not weak:

1). General n-angle star path drawing :(basically, it is the calculation of some point positions and angles, and then the line)
@param R @param R @param R @param R @param R @param R @param R @param R @param R @param R @param R @param Rreturn*/ public static Path nStarPath(int num,float R, float r) {
    Path path = new Path();
    float perDeg = 360 / num;
    float degA = perDeg / 2 / 2;
    float degB = 360 / (num - 1) / 2 - degA / 2 + degA;
    path.moveTo(
            (float) (Math.cos(rad(degA + perDeg * 0)) * R + R * Math.cos(rad(degA))),
            (float) (-Math.sin(rad(degA + perDeg * 0)) * R + R));
    for (int i = 0; i < num; i++) {
        path.lineTo(
                (float) (Math.cos(rad(degA + perDeg * i)) * R + R * Math.cos(rad(degA))),
                (float) (-Math.sin(rad(degA + perDeg * i)) * R + R));
        path.lineTo(
                (float) (Math.cos(rad(degB + perDeg * i)) * r + R * Math.cos(rad(degA))),
                (float) (-Math.sin(rad(degB + perDeg * i)) * r + R));
    }
    path.close();
    returnpath; } /** * convert Angle to radians ** @param deg Angle * @return*/ public staticfloat rad(float deg) {
    return (float) (deg * Math.PI / 180);
}
Copy the code
2). When the radius of the outer circle is related to the radius of the inner circle, regular polygonal stars and regular polygons can be formed

Ortho polygonal star:

** @param num * @param R * @param R * @param R * @param Rreturn*/ public static Path regularStarPath(int num regularStarPath,float R) {
        float degA, degB;
        if(num % 2 == 1) {// degA = 360 / num / 2/2; degB = 180 - degA - 360 / num / 2; }else {
            degA = 360 / num / 2;
            degB = 180 - degA - 360 / num / 2;
        }
        float r = (float) (R * Math.sin(rad(degA)) / Math.sin(rad(degB)));
        return nStarPath(num, R, r);
    }
Copy the code

Regular polygon:

@param num * @param R * @param R * @param R * @param Rreturn*/ public static Path regularPolygonPath(int num regularPolygonPath, int num)float R) {
        float r = (float) (R * (Math.cos(rad(360 / num / 2)))); / /!!!!! A little to solvereturnnStarPath(num, R, r); } /** * convert Angle to radians ** @param deg Angle * @return*/ public staticfloat rad(float deg) {
        return (float) (deg * Math.PI / 180);
    }
Copy the code

These two small chestnuts as a citation, should have a certain understanding of the Path of it, the following will be a formal Path to do a systematic introduction


Second, the detailed introduction of Path

Path: is a class, directly inherited from the Object, source line 879, calculate a small class but many methods, it is with the bottom of the contact, feel not good to have a look at the following public method: (basic create related, add related, set related, other) To look good, all of the following demonstrations are landscape and the origin of the canvas coordinates is moved to (800,500), and all the blue lines are helper lines


1.moveTo—-lineTo—-close

MoveTo: moveTo: moveTo: moveTo: moveTo: moveTo: moveTo: moveTo: moveTo

Path path = new Path();
path.moveTo(0, 0);
path.lineTo(100, 200);
Copy the code

Path path = new Path();
path.moveTo(0, 0);
path.lineTo(100, 200);
path.lineTo(200, 100);
Copy the code

Path path = new Path();
path.moveTo(0, 0);
path.lineTo(100, 200);
path.lineTo(200, 100);
path.close();
Copy the code


2.rMoveTo—-rLineTo

RMoveTo: start at the end of the path. RLineTo: Start at the end of the path.

Path path = new Path(); Path. RMoveTo (0, 0); path.rLineTo(100, 200); path.rLineTo(200, 100); path.close();Copy the code


3. Draw the arcTo(range, starting point, end point,)
RectF rectF = new RectF(100, 100, 500, 300); path.moveTo(0, 0); //arcTo(range, starting point, ending point, independent or not) -- defaultfalse)
//path.arcTo(rectF, 0, 45, true);
path.arcTo(rectF, 0, 45, false);

Copy the code

The rest of the Bezier curve is the big one at the end


Add path: addXXX

This is an enumeration that only uses CW(clockwise) and CCW(counterclockwise).

    public enum Direction {
        /** clockwise */
        CW  (0),    // must match enum inSkpath. h-- clockwise /** counter-clockwise */ CCW (1); // must match enuminSkpath. h-- counterclockwise Direction(int ni) {nativeInt = ni; } final int nativeInt; }Copy the code

1. Add a rectangular path:
1). Normal rectangle :addRect(left, top, Right, bottom)
RectF rectF = new RectF(100, 100, 500, 300); path.addRect(rectF, Path.Direction.CW); // Draw the rectangle clockwiseCopy the code
2). Rounded rectangle: addRoundRect(rectangular field, rounded corner x, rounded corner y)
RectF rectF = new RectF(100, 100, 500, 300); path.addRoundRect(rectF, 50, 50, Path.Direction.CW); // Draw the rounded rectangle clockwiseCopy the code
3). Control the rounded corners with 4 points: addRoundRect(rectangle field,8 numbers, direction)
RectF rectF = new RectF(100, 100, 500, 300);
path.addRoundRect(rectF, new float[]{0, 0, x,y 0, 0, x,y 450, 250, x,y 450, 200, x,y}, path.direction.cw); // Draw clockwiseCopy the code


2. Add ellipse path: addOval(rectangle field, direction)
RectF rectF = new RectF(100, 100, 500, 300);
path.addOval(rectF, Path.Direction.CW);
Copy the code


AddCircle path: addCircle(center x, center y, direction)
Path. AddCircle (100100100, path. Direction. The CW);Copy the code


4. AddArc path: addArc(rectangular field, start Angle, end Angle)
RectF rectF = new RectF(100, 100, 500, 300); Path. AddArc (rectF, 0145);Copy the code

5. Add path:
1) addPath(Path)
Path. AddCircle (100100100, path. Direction. The CW); Path otherPath = new Path(); otherPath.moveTo(0, 0); otherPath.lineTo(100, 100); path.addPath(otherPath);Copy the code
AddPath (Path, offset x, offset y)
Path. AddCircle (100100100, path. Direction. The CW); Path otherPath = new Path(); otherPath.moveTo(0, 0); otherPath.lineTo(100, 100); Path. AddPath (otherPath, 200200);Copy the code
AddPath (Path, Matrix)
Path. AddCircle (100100100, path. Direction. The CW); Path otherPath = new Path(); otherPath.moveTo(0, 0); otherPath.lineTo(100, 100); Matrix matrix = new Matrix(); matrix.setValues(newfloat[]{
        1, 0, 100,
        0, .5f, 150,
        0, 0, 1
});
path.addPath(otherPath, matrix);
Copy the code


Iv. Other operations:
1. Summary of tiny bits:
path.reset(); // Clear path and keep the fill type //path.rewind(); // clear path, leaving the data structure path.isempty ()// whether it isEmpty path.isrect (new RectF()); path.isConvex(); path.isInverseFillType(); path.set(otherPath); // Delete the path and add a new path // path.offset(200,200); // path.transform(matrix); Path tempPath = new Path(); // path.offset(200, 200, tempPath); Path.transform (matrix, tempPath); DrawPath (path, mRedPaint); drawPath(path, mRedPaint); canvas.drawPath(tempPath, mRedPaint);Copy the code
2. The difference between clockwise CW and counterclockwise CCW
1).setLastPoint(x,y): set the last point

Path saves the points in order, and setLastPoint(x,y) changes the last point

RectF rectF = new RectF(100, 100, 500, 300); path.addRect(rectF, Path.Direction.CW); // Draw the rectangle clockwise. Path.setlastpoint (200, 200); canvas.drawPath(path, mRedPaint);Copy the code

RectF rectF = new RectF(100, 100, 500, 300); path.addRect(rectF, Path.Direction.CCW); // Draw the rectangle clockwise. Path.setlastpoint (200, 200); canvas.drawPath(path, mRedPaint);Copy the code

3. Boundary calculation:
Path starPath = CommonPath.nStarPath(6, 100, 50); RectF rectF = new RectF(); // Provide your own rectangular area starPath.computeBounds(rectF,true);
canvas.drawPath(starPath, mRedPaint);
canvas.drawRect(rectF,mHelpPaint);
Copy the code

Fifth, path filling

1. Fill the initial path:
1) Left: Both are clockwise:
mRedPaint.setStyle(Paint.Style.FILL); RectF rectF = new RectF(100, 100, 500, 300); path.addRect(rectF, Path.Direction.CW); AddRect (200, 0, 400, 400, path.direction.cw); // Draw the rectangle clockwiseCopy the code
2) Right picture: horizontal clockwise, vertical counterclockwise
mRedPaint.setStyle(Paint.Style.FILL); RectF rectF = new RectF(100, 100, 500, 300); path.addRect(rectF, Path.Direction.CW); AddRect (200, 0, 400, 400, path.direction.ccw); // Draw the rectangle counterclockwiseCopy the code

It feels like the two vortices are intensifying in the same direction, and they cancel out in the opposite direction

2. The wrapping principle of padding: — A concept in the natural sciences (e.g., mathematics, physics)

Non-zero WINDING —- Default INVERSE_WINDING EVEN_ODD Anti-parity WINDING INVERSE_EVEN_ODD

  public enum FillType {
        WINDING         (0),
        EVEN_ODD        (1),
        INVERSE_WINDING (2),
        INVERSE_EVEN_ODD(3);
        FillType(int ni) {
            nativeInt = ni;
        }
        final int nativeInt;
    }
Copy the code
Path.FillType fillType = path.getFillType(); SetFillType (path.fillType.xxxxxx)// Set the typeCopy the code
// Draw the test pentpointed star path.moveto (100, 200); path.lineTo(500, 200); path.lineTo(200, 400); path.lineTo(300, 50); path.lineTo(400, 400); path.close();Copy the code

1). Non-zero surround number rule: WINDING

According to my personal understanding (fYI) : under the non-zero surround number rule

Determine whether a point is in the graph: lead ray P from the point, encounter clockwise edge +1, encounter counterclockwise edge -1 result 0, not, otherwise, inCopy the code


2). Even and parity surround number rule: EVEN_ODD

As I personally understand it (fYI) : the odd-even surround number rule

To judge whether a point is in the graph or not (non-fixed point) : From the point P, see the number of intersection points with the graph, odd, even, notCopy the code

3). Anti-non-zero surround number rules and anti-parity surround number rules:

That is, compared to the above, the filling is not filled, and the filling is not filled

So it seems that it’s very important that the graph is drawn clockwise or counterclockwise for the fill. In general, the parity principle is a little bit rough, but the non-zero principle is the default and it’s universal


6. Boolean operation OP (operation between two paths)

If the circumnavigation principle is a Path’s own struggle, then the OP is a struggle between two paths

Path right = new Path(); Path left = new Path(); left.addCircle(0, 0, 100, Path.Direction.CW); right.addCircle(100, 0, 100, Path.Direction.CW); //left.op(right, Path.Op.DIFFERENCE); //left. Op (right, path.op.reverse_difference); INTERSECT (right, path.op. INTERSECT); INTERSECT (right, path.op. INTERSECT); / / intersection - unlike you are not my / / left the op (right, the Path. The op. UNION); // set ---- together, left. Op (right, path.op.xor); Canvas. DrawPath (left, mRedPaint);Copy the code


7. Path animation: PathMeasure

Init method:
PathMeasure PathMeasure = new PathMeasure(mStarPath,false); ValueAnimator pathAnimator = valueAnimator.offloat (1, 0); // Use ValueAnimator ValueAnimator pathAnimator = ValueAnimator.offloat (1, 0); pathAnimator.setDuration(5000); pathAnimator.addUpdateListener(animation -> {floatvalue = (Float) animation.getAnimatedValue(); // Use brush dash effect + offset DashPathEffect effect = new DashPathEffect(newfloat[]{pathMeasure.getLength(), pathMeasure.getLength()},
            value * pathMeasure.getLength());
    mRedPaint.setPathEffect(effect);
    invalidate();
});
pathAnimator.start();
Copy the code
In the OnDraw method:
canvas.drawPath(mStarPath, mRedPaint);
Copy the code


Viii. Brief introduction to Bessel curve:

If Path is the window left by Canvas for advanced drawing, then Bezier curve is the door left by Path for more advanced drawing. Due to the complexity of operation, we will not go too deep here, and we will write a special article if there is a need in the future

1. Simple understanding :(figure source network)
First order Besser Second order Bessel Third order Besser
2. Example of second-order Bessel curves:
public class Bezier2View extends View { private Paint mHelpPaint; // Private Paint mPaint; // Private Path mBezierPath; Private PointF start = new PointF(0, 0); Private PointF end = new PointF(400, 0); Private PointF control = new PointF(200, 200); private Picture mPicture; // Canvas element private Point mCoo; Public Bezier2View(Context Context) {this(Context, null); } public Bezier2View(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } private voidinit() {// Bezier curve paintbrush mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.parseColor("#88EC17F3")); mPaint.setStrokeWidth(8); // resetHelpPaint(); recordBg(); // Record the coordinate system and grid when initializing -- avoid calling mBezierPath = new Path() repeatedly in Ondraw; } /** * Record the coordinate system and grid when initialization -- avoid repeated calls in Ondraw */ private voidrecordBg() {// prepare the screen size Point winSize = new Point(); mCoo = new Point(800, 500); Utils.loadWinSize(getContext(), winSize); Paint gridPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPicture = new Picture(); Canvas recordCanvas = mPicture.beginRecording(winSize.x, winSize.y); Helpdraw.drawgrid (recordCanvas, winSize, gridPaint); Helpdraw.drawcoo (recordCanvas, mCoo, winSize, gridPaint); mPicture.endRecording(); } /** * Reset the brush */ private voidresetHelpPaint() {
        mHelpPaint = new Paint();
        mHelpPaint.setColor(Color.BLUE);
        mHelpPaint.setStrokeWidth(2);
        mHelpPaint.setStyle(Paint.Style.STROKE);
        mHelpPaint.setPathEffect(new DashPathEffect(new float[] {10, 5}, 0)); mHelpPaint.setStrokeCap(Paint.Cap.ROUND); } @override public Boolean onTouchEvent(MotionEvent event) {control. X = event.getx () -mcoo. X; control.y = event.getY() - mCoo.y; invalidate();return true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); canvas.translate(mCoo.x, mCoo.y); drawHelpElement(canvas); Mbezierpath.moveto (start.x, start.y); mBezierPath.quadTo(control.x, control.y, end.x, end.y); canvas.drawPath(mBezierPath, mPaint); mBezierPath.reset(); MBezierPath Canvas.restore (); canvas.drawPicture(mPicture); ** @param canvas */ private void drawHelpElement(Canvas Canvas) {// Draw data points and control points mHelpPaint.setColor(Color.parseColor("#8820ECE2")); mHelpPaint.setStrokeWidth(20); canvas.drawPoint(start.x, start.y, mHelpPaint); canvas.drawPoint(end.x, end.y, mHelpPaint); canvas.drawPoint(control.x, control.y, mHelpPaint); ResetHelpPaint (); canvas.drawLine(start.x, start.y, control.x, control.y, mHelpPaint); canvas.drawLine(end.x, end.y, control.x, control.y, mHelpPaint); }}Copy the code

The effect is AS follows :(emulator + screen recording software +AS with point card, the demonstration on the mobile phone is very smooth)


3. Simple demonstration of third-order Besser:

mRedPaint.setStrokeWidth(5); mRedPaint.setStrokeCap(Paint.Cap.ROUND); path.moveTo(0, 0); //(cDON 1_x, CDON 1_y, Cdon 2_x, Cdon 2_y, Cdon 2_x, Cdon 2_y, Cdon 2_x, Cdon 2_y) path.cubicTo(100, 100, 300, -300, 600, 0);Copy the code

All right, Path is finished


Postscript: Jie wen standard

1. Growth record and Errata of this paper
Program source code The date of note
V0.1, The 2018-11-6 Everything you know and don’t know about Path on Android
2. More about me
Pen name QQ WeChat hobby
Zhang Feng Jie te Li 1981462002 zdl1994328 language
My lot My Jane books My CSDN Personal website
3. The statement

1—- this article is originally written by Zhang Feng Jetelie, please note 2—- all programming enthusiasts are welcome to communicate with each other 3—- personal ability is limited, if there is any error, you are welcome to criticize and point out, will be modest to correct 4—- See here, I thank you here for your love and support