Android自定义View系列---蜘蛛网六维图小实战(Kotlin)

271 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第十三天,点击查看活动详情

Android自定义View系列---蜘蛛网六维图小实战(Kotlin)

前言

源代码github 我们学习了前面的绘制基础,我们来通过实现一个蜘蛛网六维图来巩固一下之前的知识

1665633071214.jpg

实战讲解

思路步骤

  1. 绘制蜘蛛网格
  2. 绘制网格中线
  3. 绘制数据

前期准备

这是一个自定义View,所以你需要继承一下View,并且把基本的属性都初始化

package com.customize.study.chapter_1

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import java.lang.Math.sin

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

    //蛛网和中线的画笔
    private var radarPaint: Paint = Paint()
    //数据的画笔
    private var valuePaint: Paint = Paint()

    init {
        //设置描边
        radarPaint.style = Paint.Style.STROKE
        //设置绿色
        radarPaint.color = Color.GREEN
        //设置抗锯齿
        radarPaint.isAntiAlias = true
        //设置画笔宽度
        radarPaint.strokeWidth = 5f

        valuePaint.style = Paint.Style.FILL
        valuePaint.color = Color.BLUE
        valuePaint.isAntiAlias = true
        valuePaint.strokeWidth = 5f

    }

    //半径
    private var radius = 0f

    //中心x
    private var centerX = 0f

    //中心y
    private var centerY = 0f

    //蜘蛛网的个数
    private val count = 6
    
    //每个顶点的的角度
    private val angle = (Math.PI * 2f / count)

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        //获取半径  width和height的最短的90%
        radius = h.coerceAtMost(w) / 2 * 0.9f

        //获取中心坐标
        centerX = w / 2f
        centerY = h / 2f

        //刷新控件
        postInvalidate()
        super.onSizeChanged(w, h, oldw, oldh)
    }

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

        //绘制蜘蛛网格
        drawPolygon(canvas)
        //画网格中线
        drawLine(canvas)
        //画数据图
        drawRegion(canvas)

    }
}

我们在onDraw()中开始绘制我们的蜘蛛网,就三个方法对应之前的三个步骤,那我们开始一个方法开始讲解

drawPolygon(canvas)

private fdrawPolygon(canvas)un drawPolygon(canvas: Canvas) {
    //创建path
    val path = Path()
    //每个网格距离中心点距离
    val r = radius / count

    for (index in 0..count) {
        //当前网格的距离
        val curR = r * index
        //重置路径
        path.reset()
        for (j in 0..count) {
            //判断是否第一个顶点
            if (j == 0) {
                path.moveTo(centerX + curR, centerY)
            } else {
                //其他顶点 具体请看下面的图
                val x = centerX + curR * Math.cos((angle * j))
                val y = centerY + curR * sin((angle * j))
                //连成线
                path.lineTo(x.toFloat(), y.toFloat())
            }
        }
        
        //关闭一下
        path.close()
        //绘制出来
        canvas.drawPath(path, radarPaint)
    }

}

drawLine

private fun drawLine(canvas: Canvas) {
    val path = Path()
    for (index in 0..count) {
        path.reset()
        path.moveTo(centerX, centerY)

        val x = centerX + radius * Math.cos((angle * index))
        val y = centerY + radius * sin((angle * index))

        path.lineTo(x.toFloat(), y.toFloat())

        canvas.drawPath(path,radarPaint)
    }

}

drawRegion

//显示的数据
private val data = doubleArrayOf(2.0, 5.0, 1.0, 6.0, 4.0, 5.0)

private fun drawRegion(canvas: Canvas) {
    val path = Path()
    //设置透明度
    valuePaint.alpha = 127
    for (i in 0 until count) {
        val percent: Double = data.get(i) / 6
        //计算数据在六维图的位置
        val x = (centerX + radius * Math.cos(angle * i) * percent).toFloat()
        val y = (centerY + radius * sin(angle * i) * percent).toFloat()
        if (i == 0) {
            path.moveTo(x, centerY)
        } else {
            path.lineTo(x, y)
        }
        //绘制小圆点
        canvas.drawCircle(x, y, 10f, valuePaint)
    }
    //绘制填充区域
    valuePaint.style = Paint.Style.FILL_AND_STROKE
    canvas.drawPath(path, valuePaint)
}

总结

自定义绘制六维图的思路与代码就这么简单,都是使用基本属性