Android Custom View 102 (Part 13)

Draw a dashboard meter

Guowei Lv

2 minute read

Today let’s draw a dashboard meter.

I have drawn the blueprint this time:

Blueprint

Here is the code:

class Dashboard @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val BOTTOM_ANGLE = 120
    private val RADIUS = dp2px(150)
    private val ARM_LENGTH = dp2px(120)
    private val DASH_WIDTH = dp2px(2)
    private val DASH_LENGTH = dp2px(10)
    private val DASH_NUM = 12

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var effect: PathDashPathEffect
    private val dash = Path()

    init {
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = dp2px(2)
        dash.addRect(0.0f, 0.0f, DASH_WIDTH, DASH_LENGTH, Path.Direction.CW)
        val arc = Path()
        arc.addArc(
            width / 2 - RADIUS,
            height / 2 - RADIUS,
            width / 2 + RADIUS,
            height / 2 + RADIUS,
            90 + BOTTOM_ANGLE / 2.0f,
            360.0f - BOTTOM_ANGLE
        )

        val pathMeasure = PathMeasure(arc, false)

        effect = PathDashPathEffect(
            dash,
            (pathMeasure.length - dp2px(2)) / DASH_NUM,
            0.0f,
            PathDashPathEffect.Style.MORPH
        )
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        // draw arc
        canvas.drawArc(
            width / 2 - RADIUS,
            height / 2 - RADIUS,
            width / 2 + RADIUS,
            height / 2 + RADIUS,
            90 + BOTTOM_ANGLE / 2.0f,
            360.0f - BOTTOM_ANGLE,
            false,
            paint
        )
        paint.pathEffect = effect

        // draw dashes
        canvas.drawArc(
            width / 2 - RADIUS,
            height / 2 - RADIUS,
            width / 2 + RADIUS,
            height / 2 + RADIUS,
            90 + BOTTOM_ANGLE / 2.0f,
            360.0f - BOTTOM_ANGLE,
            false,
            paint
        )

        paint.pathEffect = null

        // draw arm
        canvas.drawLine(
            width / 2.0f,
            height / 2.0f,
            (cos(Math.toRadians(getAngleFromMark(3).toDouble())) * ARM_LENGTH).toFloat() + width / 2,
            (sin(Math.toRadians(getAngleFromMark(3).toDouble())) * ARM_LENGTH).toFloat() + height / 2,
            paint
        )

    }

    private fun getAngleFromMark(n: Int) =
        90 + BOTTOM_ANGLE / 2 + ((360 - BOTTOM_ANGLE) / DASH_NUM) * n
}

fun dp2px(dp: Int) =
    TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        dp.toFloat(),
        Resources.getSystem().displayMetrics
    )

Here is how the custom view looks:

Blueprint

comments powered by Disqus