日期:
来源:郭霖收集编辑:史大拿
https://juejin.cn/post/7147966036803846152
0的角度为360 / 5 * 0 1的角度为360 / 5 * 1 2的角度为360 / 5 * 2 3的角度为360 / 5 * 3 4的角度为360 / 5 * 4
class E3PolygonChartBlogView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
companion object {
// 半径
val SMALL_RADIUS = 100.dp
// 几边形
const val COUNT = 5
}
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
// 中心位置
private val centerLocation by lazy {
PointF(width / 2f, height / 2f)
}
override fun onDraw(canvas: Canvas) {
val cx = centerLocation.x
val cy = centerLocation.y
// 辅助圆
canvas.drawCircle(cx, cy, SMALL_RADIUS, paint)
// 每一个的间隔
val eachAngle = 360 / COUNT
(0 until COUNT).forEach {
val angle = it * eachAngle.toDouble()
val x =
(SMALL_RADIUS * cos(Math.toRadians(angle)) + centerLocation.x).toFloat()
val y =
(SMALL_RADIUS * sin(Math.toRadians(angle)) + centerLocation.y).toFloat()
paint.color = colorRandom
// 绘制每一个小圆
canvas.drawCircle(x, y, 10.dp, paint)
}
}
}
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val path = Path()
override fun onDraw(canvas: Canvas) {
// 每一个的间隔
val eachAngle = 360 / COUNT
(0 until COUNT).forEach {
val angle = it * eachAngle.toDouble()
val x =
(SMALL_RADIUS * cos(Math.toRadians(angle)) + centerLocation.x).toFloat()
val y =
(SMALL_RADIUS * sin(Math.toRadians(angle)) + centerLocation.y).toFloat()
// 连接每一个点
if (it == 0) {
path.moveTo(x, y)
} else {
path.lineTo(x, y)
}
}
path.close() // 闭合
paint.strokeWidth = 2.dp
paint.style = Paint.Style.STROKE
canvas.drawPath(path, paint) // 绘制
path.reset()
}
class E3PolygonChartBlogView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
companion object {
// 半径
val SMALL_RADIUS = 100.dp
// 几边形
const val COUNT = 5
// 有几条边
const val NUMBER = 3
// 每一条边的间隔
val INTERVAL = 20.dp
}
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
// 中点
private val centerLocation by lazy {
PointF(width / 2f, height / 2f)
}
private val path = Path()
override fun onDraw(canvas: Canvas) {
// 每一个的间隔
val eachAngle = 360 / COUNT
// 循环有几条边
(0 until NUMBER).forEachIndexed { index, element ->
// 循环每一条边有几个点
(0 until COUNT).forEach { count ->
// 半径 = 当前是第几条边 * 间距 + 最中间的距离
val radius = element * INTERVAL + SMALL_RADIUS
val angle = count * eachAngle.toDouble()
val x =
(radius * cos(Math.toRadians(angle)) + centerLocation.x).toFloat()
val y =
(radius * sin(Math.toRadians(angle)) + centerLocation.y).toFloat()
if (count == 0) {
path.moveTo(x, y)
} else {
path.lineTo(x, y)
}
}
path.close() // 闭合
paint.strokeWidth = 2.dp
paint.style = Paint.Style.STROKE
canvas.drawPath(path, paint)
paint.reset()
}
}
}
override fun onDraw(canvas: Canvas) {
// 每一个的间隔
val eachAngle = 360 / COUNT
// 循环有几条边
(0 until NUMBER).forEachIndexed { index, element ->
// 循环每一条边有几个点
(0 until COUNT).forEach { count ->
// 半径 = 当前是第几条边 * 间距 + 最中间的距离
val radius = element * INTERVAL + SMALL_RADIUS
val angle = count * eachAngle.toDouble()
val x =
(radius * cos(Math.toRadians(angle)) + centerLocation.x).toFloat()
val y =
(radius * sin(Math.toRadians(angle)) + centerLocation.y).toFloat()
.....
// 当前是最后一层
if (index == NUMBER - 1) {
// 最内层x,y 坐标
val stopX =
(SMALL_RADIUS * cos(Math.toRadians(angle)) + centerLocation.x).toFloat()
val stopY =
(SMALL_RADIUS * sin(Math.toRadians(angle)) + centerLocation.y).toFloat()
canvas.drawLine(x, y, stopX, stopY, paint)
// 连接中心点
// canvas.drawLine(x, y, centerLocation.x, centerLocation.y, paint)
}
}
path.close() // 闭合
canvas.drawPath(path, paint)
paint.reset()
}
}
10边形 每一条边有7个点 最中心的半径为 20.dp 每一个边的间距 = 20.dp
companion object {
// 半径
val SMALL_RADIUS = 20.dp
// 几边形
const val COUNT = 10
// 有几条边
const val NUMBER = 7
// 每一条边的间隔
val INTERVAL = 20.dp
}
@SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) {
// 每一个的间隔
val eachAngle = 360 / COUNT
// 循环有几条边
(0 until NUMBER).forEachIndexed { index, element ->
// 循环每一条边有几个点
(0 until COUNT).forEach { count ->
// 半径 = 当前是第几条边 * 间距 + 最中间的距离
val radius = element * INTERVAL + SMALL_RADIUS
val angle = count * eachAngle.toDouble()
val x =
(radius * cos(Math.toRadians(angle)) + centerLocation.x).toFloat()
val y =
(radius * sin(Math.toRadians(angle)) + centerLocation.y).toFloat()
...
// 绘制最外层和内层连接线
...
// 设置文字
if (index == NUMBER - 1) {
val text = "文字${count}"
val rect = Rect()
// 计算文字宽高 计算完成之后会把值赋值给rect
paint.getTextBounds(text, 0, text.length, rect)
val textWidth = rect.width()
val textHeight = rect.height()
val tempRadius = radius + textHeight
val textX =
(tempRadius * cos(Math.toRadians(angle)) + centerLocation.x).toFloat() - textWidth / 2f
val textY =
(tempRadius * sin(Math.toRadians(angle)) + centerLocation.y).toFloat()
paint.textSize = 16.dp
paint.style = Paint.Style.FILL
paint.color = E3PolygonChartView.TEXT_COLOR
// 绘制最外层文字
canvas.drawText(text, textX, textY, paint)
}
}
...
}
}
override fun onDraw(canvas: Canvas) {
// 绘制网格
...
// 绘制数据
drawArea(canvas)
}
var data = listOf(3f, 2f, 3f, 1f, 1f)
private fun drawArea(canvas: Canvas) {
data.forEachIndexed { index, value ->
val location = getLocation(index, value)
if (index == 0) {
path.moveTo(location.x, location.y)
} else {
path.lineTo(location.x, location.y)
}
}
path.close()
paint.style = Paint.Style.STROKE
paint.color = Color.RED
canvas.drawPath(path, paint) // 绘制边
paint.style = Paint.Style.FILL
paint.alpha = (255 * 0.1).toInt()
canvas.drawPath(path, paint) // 绘制内边
path.reset()
}
/*
* 作者:史大拿
* 创建时间: 9/27/22 2:54 PM
* @number 第几个点
* @count 第几条边
*/
private fun getLocation(number: Int, count: Float): PointF = let {
// 角度
val angle = 360 / COUNT * number
// 半径
val radius = (count - 1) * INTERVAL + SMALL_RADIUS
val x =
(radius * cos(Math.toRadians(angle.toDouble())) + centerLocation.x).toFloat()
val y =
(radius * sin(Math.toRadians(angle.toDouble())) + centerLocation.y).toFloat()
return PointF(x, y)
}
dx = centerLocation.x - event.x dy = centerLocation.x - event.y
@param startP: 开始点
@param endP: 结束点
fun PointF.angle(endP: PointF): Float {
val startP = this
// 原始位置
val angle = if (startP.x >= endP.x && startP.y >= endP.y) {
Log.e("szjLocation", "end在start右下角")
0
} else if (startP.x >= endP.x && startP.y <= endP.y) {
Log.e("szjLocation", "end在start右上角")
270
} else if (startP.x <= endP.x && startP.y <= endP.y) {
Log.e("szjLocation", "end在start左上角")
180
} else if (startP.x <= endP.x && startP.y >= endP.y) {
Log.e("szjLocation", "end在start左下角")
90
} else {
0
}
// 计算距离
val dx = startP.x - endP.x
val dy = startP.y - endP.y
// 弧度
val radian = abs(atan(dy / dx))
// 弧度转角度
var a = Math.toDegrees(radian.toDouble()).toFloat()
if (startP.x <= endP.x && startP.y >= endP.y) {
// 左下角
a = 90 - a
} else if (startP.x >= endP.x && startP.y <= endP.y) {
// 右上角
a = 90 - a
}
return a + angle
}
var offsetAngle = 0f // 偏移角度
private var downAngle = 0f // 按下角度
private var originAngle = 0f // 原始角度
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
downAngle = centerLocation.angle(PointF(event.x, event.y))
originAngle = offsetAngle
}
MotionEvent.ACTION_MOVE -> {
parent.requestDisallowInterceptTouchEvent(true)
// 当前偏移角度 = 现在角度 - 按下角度 + 原始角度
offsetAngle =
centerLocation.angle(PointF(event.x, event.y)) - downAngle + originAngle
Log.e("szjOffset","$offsetAngle")
}
MotionEvent.ACTION_UP -> {
}
}
invalidate()
return true
}
override fun onDraw(canvas: Canvas) {
// 每一个的间隔
val eachAngle = 360 / COUNT
// 循环有几条边
(0 until NUMBER).forEachIndexed { index, element ->
// 循环每一条边有几个点
(0 until COUNT).forEach { count ->
val angle = count * eachAngle.toDouble() + offsetAngle // TODO 设置角度
val x =
(radius * cos(Math.toRadians(angle)) + centerLocation.x).toFloat()
val y =
(radius * sin(Math.toRadians(angle)) + centerLocation.y).toFloat()
if (count == 0) {
path.moveTo(x, y)
} else {
path.lineTo(x, y)
}
// 连接最外层和最内层
....
// 设置文字
....
}
....
canvas.drawPath(path, paint)
path.reset()
}
// 绘制数据
drawArea(canvas)
}
https://gitee.com/lanyangyangzzz/custom-view-project