这次学习了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)
}
}
}