import android.content.Context
import android.graphics.*
import android.os.Build
import android.util.AttributeSet
import android.view.View
import androidx.annotation.RequiresApi
import kotlin.math.cos
import kotlin.math.roundToInt
import kotlin.math.sin
class Ring : View {
//半徑
private var radius = 0f
//內部填充圓半徑
private var radiusInner = 0f
// 中心點
private var centerX = 0f
private var centerY = 0f
// 開始角度
private var startAngle = 0f
var colorAndScore = mutableListOf<Pair<Int, Int>>()
set(value) {
field = value
invalidate()
}
/**
* 扇形的角度集合
*/
private var sweepAngles = mutableListOf<Float>()
/**
* 百分比集合
*/
private var percents = mutableListOf<Int>()
private val mPaint = Paint()
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
/**
* 強制高等於寬
*/
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(widthMeasureSpec))
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
startAngle = 0f
radius = (width * 0.5).toFloat()
radiusInner = (radius * 0.7).toFloat()
this.centerX = (right - left) / 2f
this.centerY = (bottom - top) / 2f
val total = colorAndScore.sumBy { it.second }
if (total <= 0) {
initDefualtCircle(canvas)
initCircle(canvas)
return
}
sweepAngles.clear()
colorAndScore.forEach {
sweepAngles.add((it.second * 360.00 / total).roundToInt().toFloat())
percents.add((it.second * 100.00 / total).roundToInt())
}
initArc(canvas)
initCircle(canvas)
initText(canvas)
}
/**
* 繪製扇形圖
* */
private fun initArc(canvas: Canvas?) {
// 矩形區域
val rect = RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius)
mPaint.isAntiAlias = true
for (i in colorAndScore.indices) {
val pair = colorAndScore[i]
mPaint.color = pair.first
canvas?.drawArc(rect, startAngle, sweepAngles[i], true, mPaint)
startAngle += sweepAngles[i]
}
}
/**
* 繪製百分比文字
*/
private fun initText(canvas: Canvas?) {
val paint = Paint()
paint.color = Color.WHITE
paint.textSize = (radius - radiusInner) / 2
sweepAngles.forEachIndexed { index, sweepAngle ->
val angle = ((sweepAngle / 2) + if (index > 0) {
sweepAngles.subList(0, index).sum()
} else {
0f
})
var textX = (centerX + ((radius + radiusInner) / 2f) * cos(angle * Math.PI / 180)).toFloat()
var textY = (centerY + ((radius + radiusInner) / 2f) * sin(angle * Math.PI / 180)).toFloat()
val percentText = "${percents[index]}%"
val widthPercentText = paint.measureText(percentText)
val rect = Rect()
paint.getTextBounds(percentText, 0, percentText.length, rect)
val heightPercentText = rect.height()
canvas?.drawText(percentText, textX - widthPercentText / 2, textY + heightPercentText / 2, paint)
}
}
/**
*中心圓
*
*/
private fun initCircle(canvas: Canvas?) {
mPaint.color = Color.parseColor("#ffffff")
canvas?.drawCircle(centerX, centerY, radiusInner, mPaint)
}
/**
*默認灰色圓
*
*/
private fun initDefualtCircle(canvas: Canvas?) {
mPaint.color = Color.parseColor("#EEEEEE")
canvas?.drawCircle(centerX, centerY, radius, mPaint)
}
}
傳入數據總和爲0時顯示灰色圓環
傳入數據總和大於0時顯示配置顏色圓環並顯示百分比
val colorAndAngle = mutableListOf<Pair<Int, Int>>()
val mColors = intArrayOf(
ContextCompat.getColor(this, R.color.text_black),
ContextCompat.getColor(this, R.color.text_pink),
ContextCompat.getColor(this, R.color.text_pink_4),
ContextCompat.getColor(this, R.color.text_gold_1)
)
for (i in mColors.indices) {
val pair = Pair(mColors[i], when (i) {
0 -> {
100
}
1 -> {
1000
}
2 -> {
208
}
else -> {
399
}
}.toInt())
colorAndScore.add(pair)
}
ring.colorAndScore = colorAndScore
參考
https://www.jianshu.com/p/346cbfe2552e
https://blog.csdn.net/li_hlkjl/article/details/73929417