First, the drawing method of trajectory

The difficulty of trajectory is mainly the measurement of position. For example, we want to get the instrument panel below (black part) :

The preset constants

val DASH_WIDTH = 2f.dp2px   // Width of the scale
val DASH_LENGTH = 10f.dp2px   // The length of the scale
val RAIUS = 100f.dp2px   // Radius of the arc
val LENGTH = 80f.dp2px   // The length of the pointer
const val OPEN_ANGLE = 120f    // Angle of the arc opening
Copy the code

Dp2px is an extended property of Float

/** * The extended property of Float */
val Float.dp2px
        get() = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            this,
            Resources.getSystem().displayMetrics
        )
Copy the code

1, draw an arc

val paint = Paint(Paint.ANTI_ALIAS_FLAG)   // Arc pen
val arcPath = Path()  // Arc path
paint.strokeWidth = 2f  // The brush width
paint.style = Paint.Style.STROKE  // Brush mode
// Set the path
arcPath.addArc(
    width / 2f - RAIUS,
    height / 2f - RAIUS,
    width / 2f + RAIUS,
    height / 2f + RAIUS,
    90 + OPEN_ANGLE / 2f,
    360 - OPEN_ANGLE
)
// Draw an arc in onDraw
canvas.drawPath(arcPath, paint)
Copy the code

Results show

The main point of drawing an arc lies in the setting of the Path. Define a rectangle by setting four points on the top left and bottom right, and then draw an arc inside the rectangle.

2. Draw the scale

The idea of calibration is to draw a dotted line, and set the length, width and spacing of the solid section of the dotted line. The path of the dotted line is the same as that of the arc.

Set a brush

private val paintEffect = Paint(Paint.ANTI_ALIAS_FLAG)  / / calibration
paintEffect.strokeWidth = 2f     // Two attributes of the brush
paintEffect.style = Paint.Style.STROKE

Copy the code

Set two paths, one for the solid segment of the dotted line and one for the entire dotted line.

private val dashPath = Path()   // The Path of the dotted solid segment
private val arcPath = Path()    // Dashed line Path
// The Path of the dotted solid segment
dashPath.addRect(0f, 0f, DASH_WIDTH, DASH_LENGTH, Path.Direction.CCW)
// Dashed line Path
arcPath.addArc(
    width / 2f - RAIUS,
    height / 2f - RAIUS,
    width / 2f + RAIUS,
    height / 2f + RAIUS,
    90 + OPEN_ANGLE / 2f,
    360 - OPEN_ANGLE)
Copy the code

So the question is, how do you calculate the spacing of the dotted lines, how do you calculate the spacing of the scales?

1) Calculate the length of the dashed line’s Path

// Measure the length of the Path
val arcPathMessure = PathMeasure(arcPath, false)   //false path is not sealed
Copy the code

2) Calculate the spacing of dotted lines

 (arcPathMessure.length - DASH_WIDTH) / 20f   // Subtract the width of the dotted line here
Copy the code

3) Set the properties of the dashed line to the brush

paintEffect.pathEffect =
    PathDashPathEffect(
        dashPath,
        (arcPathMessure.length - DASH_WIDTH) / 20f,
        0f,
        PathDashPathEffect.Style.ROTATE
    )
Copy the code

Finally, we finish the scale work

canvas.drawPath(arcPath, paintEffect)
Copy the code

Results show

3. Draw the pointer

Drawing Pointers requires a basic understanding of trigonometric functions

Sine: sin α = y/ R Cosine: cos α = x/ R Tangent: Tan α = y/x secant: SEC α =1/cosα = r/x cosecant: CSC α =1/sinα = r/y cotangent: cotangent α =1/ tan alpha = x/yCopy the code

Calculate the Angle of the pointer, such as the fifth scale (the leading scale does not count)

90 + OPEN_ANGLE / 2f + (360 - OPEN_ANGLE) / 20f * 5
Copy the code

Convert to code

val radians = Math.toRadians((90 + OPEN_ANGLE / 2f + (360 - OPEN_ANGLE) / 20f * 5).toDouble())
Copy the code

Drawing a pointer is essentially drawing a line that specifies the starting and ending points on the XY axis

/ / pointer
canvas.drawLine(
    width / 2f,
    height / 2f,
    width / 2f + cos(radians).toFloat() * LENGTH,
    height / 2f + sin(radians).toFloat() * LENGTH,
    paint
)
Copy the code

Results show

Pull the code out of the scale

/** * draw pointer *@param */ pointer the scale on the pointer
private fun drawPointer(canvas: Canvas, pointer: Int) {
    / / Angle
    val radians =
        Math.toRadians((90 + OPEN_ANGLE / 2f + (360 - OPEN_ANGLE) / 20f * pointer).toDouble())
    / / pointer
    canvas.drawLine(
        width / 2f,
        height / 2f,
        width / 2f + cos(radians).toFloat() * LENGTH,
        height / 2f + sin(radians).toFloat() * LENGTH,
        paint
    )
}
Copy the code

4. Knowledge summary

1) Pay attention to the drawing method of solid and dotted lines and the use of PathDashPathEffect

2) Pay attention to the measurement of Path and the use of PathMessure

3) Pay attention to the calculation of dotted line spacing, which needs to subtract the width of a solid dotted line

4) Pay attention to the use of trig functions

Two, pie chart drawing method

The preset constants

private val PIE_RAIUS = 100f.dp2px   // Pie radius
private val ANGLES = floatArrayOf(60f, 90f, 120f, 90f)   // Pie chart Angle
private val COLORS = listOf<Int>(   // The pie chart color
    Color.parseColor("#C2185B"),
    Color.parseColor("#00ACC2"),
    Color.parseColor("#558B2D"),
    Color.parseColor("#5D4024")
)
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)   // The pie brush
Copy the code

1. Draw pie charts

/** * draw the pie chart */
private fun drawPie(canvas: Canvas) {
    var tempAngle = 0f
    for ((index, angle) in ANGLES.withIndex()) {
        paint.color = COLORS[index]
        canvas.drawArc(
            width / 2f - PIE_RAIUS,
            height / 2f - PIE_RAIUS,
            width / 2f + PIE_RAIUS,
            height / 2f + PIE_RAIUS,
            tempAngle,  / / starting Angle
            angle,   // Offset (tempAngle+ Angle)
            true./ / the sealing
            paint
        )
        tempAngle += angle
    }
}
Copy the code

2. Draw pie charts with offsets

/** * Draw pie chart with offset *@param TranslateValue Offset amount *@param Num Number of offset */
private fun drawPieWithTranslate(canvas: Canvas, translateValue: Int,num:Int) {
    var tempAngle = 0f
    for ((index, angle) in ANGLES.withIndex()) {
        paint.color = COLORS[index]
        if (index == num) {   // Which offset
            canvas.save()
            var radius = Math.toRadians((tempAngle + angle / 2).toDouble())  // Calculate the Angle
            canvas.translate(   // The offset API also involves the calculation of trigonometric functions
                cos(radius).toFloat() * translateValue,
                sin(radius).toFloat() * translateValue
            )
        }
        canvas.drawArc(
            width / 2f - PIE_RAIUS,
            height / 2f - PIE_RAIUS,
            width / 2f + PIE_RAIUS,
            height / 2f + PIE_RAIUS,
            tempAngle,
            angle,
            true,
            paint
        )
        tempAngle += angle
        if (index == num) {  // Which recovery
            canvas.restore()
        }
    }
}
Copy the code

rendering

3. Knowledge summary

1) The offset Api is Canvas.translate

2) Pay attention to the setting of Angle of drawArc method

3) Note the use of canvas.save and canvas.restore

Personal study notes