【Kotlin】自定义View(二)

这次学习了Path的基本用法,文章后面有一个小练习,是画一个蜘蛛网,用于显示一个对象的属性的功能,先上一个图。这里写图片描述

要画这个图之前先分析一下这个图

  • 一个N正边形的底层
  • 一条连接中心点和顶点的线
  • 每个顶点有文字内容
  • 遮罩层蓝色区域这块
  • 蓝色区域的顶点画的圆点

    第一步

/**
 - 画蜘蛛网
*/
private fun setDrawCobweb(canvas: Canvas) {
  val cobwebPath = Path()
  for (i in 1 until count) {
      val cur = space * i
      cobwebPath.reset()
      for (j in 0 until count) {
          val x = (cur * Math.cos(radian * j))
          val y = (cur * Math.sin(radian * j))
          if (j == 0) {
              cobwebPath.moveTo(x.toFloat(), y.toFloat())
          } else {
              cobwebPath.lineTo(x.toFloat(), y.toFloat())
          }
      }
      cobwebPath.close()
      canvas.drawPath(cobwebPath, mPaint)
  }
}

先看代码,外层的for循环是画嵌套的多边形,里面的for循环是画每层的多边形的边。radian代表每个单元格的弧度,一个圆的弧度是2π,所以我们使用(Math.PI * 2 / count)就算到每个单格的弧度radian,cur是正在画的圆的半径,再使用三角函数取得每个点的坐标。

  • 获取坐标的公式
x=r*cos0
y=r*sin0

使用Path的moveTo方法把第一点移到(cur,0)点上面去,然后用lineTo将每个点连接在一起,最后我们使用close闭合整个Path。这样我们的整个蜘蛛网就画完了。

第二步

这一步非常简单,画从中心点到最外层多边形顶点的射线。

/**
 * 画射线
 */
private fun setDrawRays(canvas: Canvas) {
    val linePath = Path()
    for (i in 0 until count) {
        linePath.moveTo(0f, 0f)
        val x = (radius * Math.cos(radian * i))
        val y = (radius * Math.sin(radian * i))
        linePath.lineTo(x.toFloat(), y.toFloat())
    }
    canvas.drawPath(linePath, mPaint)
}

同样还是使用三角函数获取坐标,不过其中的半径取得最外层的半径,linePath.moveTo(0f, 0f)这句话的意思每画完一条线,都要把这条线的终点移动到中心位置,这样才能保证每条线都是从中心出发的。

第三步

第三步画文字内容,这一步是最不好画的,需要精雕细琢,一个个点去移动文字的位置,原文是使用象限来做的,但是我拿代码过来运行就怎么都不称,不知道是不是我复制代码错了问题(o _ o),只能自己想办法了,然后我在象限判断的基础上加了弧度的判断,吧四个象限分成八段弧度,每个象限两段弧度,当然还可以分的更精确。

/**
* 画文字
*/
private fun setDrawText(canvas: Canvas) {
  for (i in 0 until count) {
      val textHeight = textPaint.measureText(i.toString())
      val x = ((radius) * Math.cos(radian * i)).toFloat()
      val y = ((radius) * Math.sin(radian * i)).toFloat()
      if (x >= 0 && y >= 0) {
          if (radian * i < (Math.PI * 2) / 8) {
              canvas.drawText(i.toString(), x + magin * 3, y + textHeight / 2, textPaint)
          } else {
              canvas.drawText(i.toString(), x - textHeight / 2, y + textHeight * 2, textPaint)
          }
      } else if (x < 0 && y > 0) {
          if (radian * i < (Math.PI * 2) / 8 * 3) {
              canvas.drawText(i.toString(), x - textHeight, y + textHeight * 2, textPaint)
          } else {
              canvas.drawText(i.toString(), x - textHeight - magin * 3, y + textHeight / 2, textPaint)
          }
      } else if (x < 0 && y < 0) {
          if (radian * i < (Math.PI * 2) / 8 * 5) {
              canvas.drawText(i.toString(), x - textHeight, y, textPaint)
          } else {
              canvas.drawText(i.toString(), x - textHeight / 2, y - textHeight / 2, textPaint)
          }
      } else if (x > 0 && y < 0) {
          canvas.drawText(i.toString(), x, y, textPaint)
      }
  }
}

magin 是我自己定义的小间距,微调时使用,最重要的radian * i < (Math.PI * 2) / 8行代码,就是来判断当前的弧度是否还在第一段,后面的* 3代表的第三段弧度,最后一个象限没有判断是因为文字显示位置没有必要调整。

第四步

    /**
     * 画区域
     */
    private fun setDrawArea(canvas: Canvas) {
        val areaPaint = Paint()
        areaPaint.strokeWidth = 10f
        areaPaint.isAntiAlias = true
        areaPaint.style = Paint.Style.FILL

        val areaPath = Path()
        for (i in 0 until count) {
            val random: Int = (6 + Math.random() * (count - 6)).toInt()
            val cur = space * random
            val x = (cur * Math.cos(radian * i)).toFloat()
            val y = (cur * Math.sin(radian * i)).toFloat()
            if (i == 0) {
                areaPath.moveTo(x, y)
            } else {
                areaPath.lineTo(x, y)
            }
            areaPaint.color = Color.DKGRAY
            canvas.drawCircle(x, y, 15f, areaPaint)
        }
        areaPaint.color = Color.BLUE
        areaPaint.alpha = 120
        areaPath.close()
        canvas.drawPath(areaPath, areaPaint)
    }

这里画这个蓝色区域的遮罩层,还有顶点的圆,我用了一个6到count的一个随机数生成半径cur,用这个半径确定每个点的坐标

  val random: Int = (6 + Math.random() * (count - 6)).toInt()
  val cur = space * random
  val x = (cur * Math.cos(radian * i)).toFloat()
  val y = (cur * Math.sin(radian * i)).toFloat()

已经有了每个点的坐标,在这个坐标上画一个圆点难得不是一个很容易的事。15f是这个圆点半径,可以自己定义。

 canvas.drawCircle(x, y, 15f, areaPaint)

到这里整个蜘蛛网雷达就画完了,这是我自己练习的一个画图,如果要使用到项目中,这里面的文字内容和,产生的遮罩区域需要按照自己的逻辑来改动。

全部代码

class RadarView : View {
    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    private val TAG: String = "RadarView"

    private var mPaint: Paint
    private val textPaint: Paint

    //网格最大半径
    private var radius: Float = 0f

    private val count = 11

    private var centerX: Float = 0f
    private var centerY: Float = 0f
    //一周对应的角度为360度(角度),对应的弧度为2π弧度。计算弧度
    private var radian: Double

    private val magin = 4

    private var space: Float = 0f

    init {
        radian = (Math.PI * 2 / count)
        mPaint = Paint()
        mPaint.color = Color.BLACK
        mPaint.strokeWidth = 5f
        mPaint.isAntiAlias = true
        mPaint.style = Paint.Style.STROKE

        textPaint = Paint()
        textPaint.color = Color.BLACK
        textPaint.strokeWidth = 1f
        textPaint.isAntiAlias = true
        textPaint.style = Paint.Style.FILL
        textPaint.textSize = 50f             // 设置字体大小
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        centerX = w / 2f
        centerY = h / 2f
        radius = Math.min(centerX, centerY) * 0.9f
        space = radius / (count - 1)

    }


    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.translate(centerX, centerY)

        setDrawCobweb(canvas)
        setDrawRays(canvas)
        setDrawText(canvas)
        setDrawArea(canvas)
    }

    /**
     * 画区域
     */
    private fun setDrawArea(canvas: Canvas) {
        val areaPaint = Paint()
        areaPaint.strokeWidth = 10f
        areaPaint.isAntiAlias = true
        areaPaint.style = Paint.Style.FILL

        val areaPath = Path()
        for (i in 0 until count) {
            val random: Int = (6 + Math.random() * (count - 6)).toInt()
            val cur = space * random
            val x = (cur * Math.cos(radian * i)).toFloat()
            val y = (cur * Math.sin(radian * i)).toFloat()
            if (i == 0) {
                areaPath.moveTo(x, y)
            } else {
                areaPath.lineTo(x, y)
            }
            areaPaint.color = Color.DKGRAY
            canvas.drawCircle(x, y, 15f, areaPaint)
        }
        areaPaint.color = Color.BLUE
        areaPaint.alpha = 120
        areaPath.close()
        canvas.drawPath(areaPath, areaPaint)
    }

    /**
     * 画文字
     */
    private fun setDrawText(canvas: Canvas) {
        for (i in 0 until count) {
            val textHeight = textPaint.measureText(i.toString())
            val x = ((radius) * Math.cos(radian * i)).toFloat()
            val y = ((radius) * Math.sin(radian * i)).toFloat()
            if (x >= 0 && y >= 0) {
                if (radian * i < (Math.PI * 2) / 8) {
                    canvas.drawText(i.toString(), x + magin * 3, y + textHeight / 2, textPaint)
                } else {
                    canvas.drawText(i.toString(), x - textHeight / 2, y + textHeight * 2, textPaint)
                }
            } else if (x < 0 && y > 0) {
                if (radian * i < (Math.PI * 2) / 8 * 3) {
                    canvas.drawText(i.toString(), x - textHeight, y + textHeight * 2, textPaint)
                } else {
                    canvas.drawText(i.toString(), x - textHeight - magin * 3, y + textHeight / 2, textPaint)
                }
            } else if (x < 0 && y < 0) {
                if (radian * i < (Math.PI * 2) / 8 * 5) {
                    canvas.drawText(i.toString(), x - textHeight, y, textPaint)
                } else {
                    canvas.drawText(i.toString(), x - textHeight / 2, y - textHeight / 2, textPaint)
                }
            } else if (x > 0 && y < 0) {
                canvas.drawText(i.toString(), x, y, textPaint)
            }
        }
    }

    /**
     * 画射线
     */
    private fun setDrawRays(canvas: Canvas) {
        val linePath = Path()
        for (i in 0 until count) {
            linePath.moveTo(0f, 0f)
            val x = (radius * Math.cos(radian * i))
            val y = (radius * Math.sin(radian * i))
            linePath.lineTo(x.toFloat(), y.toFloat())
        }
        canvas.drawPath(linePath, mPaint)
    }

    /**
     * 画蜘蛛网
     */
    private fun setDrawCobweb(canvas: Canvas) {
        val cobwebPath = Path()
        for (i in 1 until count) {
            val cur = space * i
            cobwebPath.reset()
            for (j in 0 until count) {
                val x = (cur * Math.cos(radian * j))
                val y = (cur * Math.sin(radian * j))
                if (j == 0) {
                    cobwebPath.moveTo(x.toFloat(), y.toFloat())
                } else {
                    cobwebPath.lineTo(x.toFloat(), y.toFloat())
                }
            }
            cobwebPath.close()
            canvas.drawPath(cobwebPath, mPaint)
        }
    }
}


猜你喜欢

转载自blog.csdn.net/sinat_32089827/article/details/79164765