项目开发中涉及统计功能,花时间自己做了饼状图组件,先贴实现样式以及代码,再讲如何使用。
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import com.rp.statisticalchart.R
import kotlin.math.cos
import kotlin.math.sin
/**
* @ClassName: PieChartView
* @Description: <p>饼状图View (请用一句话描述该类做什么)</p>
* @author:RainPan
* @date: 2021/3/4 10:54
* ${tags}$
*/
class PieChartWidget : View {
private var tangle = 0f
private val delayMillis = 10
private val mColor = Color.GREEN
private var mCircleWidth = 0f
private lateinit var mPaint:Paint
/** 绑定的数据*/
private val values = mutableListOf<DataBean>()
/** 扇形图中央显示数据类型名称*/
private var centerName = "资源数量"
/** 扇形图中央显示单位*/
private var centerUnit = "个"
/** 显示类型 0显示具体数量 1显示百分比*/
private var showType = 0
private var total = 0f;
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
constructor(context: Context?, values: MutableList<DataBean>) : super(context) {
this.values.addAll(values)
}
constructor(context: Context?, values: MutableList<DataBean>, centerName: String, centerUnit: String, showType: Int) : super(context) {
this.values.addAll(values)
this.centerName = centerName
this.centerUnit = centerUnit
this.showType = showType
}
/**
* Implement this to do your drawing.
*
* @param canvas the canvas on which the background will be drawn
*/
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
mCircleWidth = (if (width / 2 >= height / 2) height / 2 / 5 else width / 2 / 5).toFloat()
val center = (width / 2).toFloat()
val radius = (if (width / 2 >= height / 2) height / 2 - height / 4 else width / 2 - mCircleWidth - height / 4).toFloat()
var ttangle = -90f
mPaint = Paint()
mPaint.strokeWidth = mCircleWidth
mPaint.isAntiAlias = true
mPaint.style = Paint.Style.STROKE
mPaint.color = mColor
val oval = RectF(center - radius, height / 2 - radius, center + radius, height / 2 + radius)
val oval_white = RectF(center - radius + mCircleWidth / 2, height / 2 - radius + mCircleWidth / 2,
center + radius - mCircleWidth / 2, height / 2 + radius - mCircleWidth / 2)
if (values.size > 0) {
for (i in values) {
total += i.num
}
if (total > 0) {
val center_y = height / 2
val heightInt = resources.displayMetrics.heightPixels
//连接线的画笔
val label_line = Paint()
label_line.strokeWidth = height / 427f
label_line.isAntiAlias = true
label_line.style = Paint.Style.STROKE
//连接线后面小圆的画笔
val little_circle = Paint()
little_circle.strokeWidth = 2f
little_circle.isAntiAlias = true
little_circle.style = Paint.Style.STROKE
val radius_little_circle = height / 160f
//连接线上文字
val textSize = height / 40f
val line_text = Paint()
line_text.color = Color.rgb(225, 225, 225)
line_text.isAntiAlias = true
line_text.textSize = textSize
line_text.typeface = Typeface.DEFAULT
val radian = FloatArray(values.size)
val d = FloatArray(values.size)
for (i in 0 until values.size) {
radian[i] = (values[i].num / total * 360)
d[i] = if (i > 0) (radian[i-1] + radian[i]) / 2 else radian[i] / 2
label_line.color = resources.getColor(values[i].color.toInt())
little_circle.color = resources.getColor(values[i].color.toInt())
var dSize = 0f
for (num in 0..i) {
dSize += d[num]
}
addLine(i, dSize, center, center_y, radius, canvas, label_line, radius_little_circle, little_circle, line_text, textSize)
mPaint.strokeWidth = mCircleWidth
mPaint.color = resources.getColor(values[i].color.toInt())
val d = (values[i].num / total * 360)
//画圆弧
val paint_white = Paint()
paint_white.strokeWidth = 15f
paint_white.color = resources.getColor(R.color.colorWhite)
paint_white.isAntiAlias = true
paint_white.style = Paint.Style.STROKE
canvas?.let {
it.drawArc(oval, ttangle.toFloat(), d, false, mPaint)
it.drawArc(oval_white, 0f, 360f, false, paint_white)
}
ttangle += d
}
canvas?.let {
//画中间的圆
val paint_circle = Paint()
paint_circle.isAntiAlias = true
paint_circle.color = resources.getColor(R.color.color_midcircle)
it.drawCircle(center, center_y.toFloat(), radius - mCircleWidth / 2, paint_circle)
val paintText = Paint()
paintText.color = Color.rgb(225, 225, 225)
val mid_text = height / 20f
val mid_num = height / 22f
paintText.textSize = mid_text
paintText.typeface = Typeface.DEFAULT
paintText.isAntiAlias = true
val text_width = paintText.measureText(centerName)
it.drawText(centerName, center - text_width / 2, height / 2 - mid_text, paintText)
val textNum = Paint()
textNum.color = Color.rgb(225, 225, 225)
textNum.textSize = mid_num
textNum.typeface = Typeface.DEFAULT
textNum.isAntiAlias = true
var num = 0
for (i in values.indices) {
num += values[i].num.toInt()
}
val num_width = textNum.measureText("$num$centerUnit")
val num_width2 = textNum.measureText("$num")
it.drawText("$num", center - num_width / 2, height / 2 + mid_num, textNum)
it.drawText("$centerUnit", center - num_width / 2 + num_width2, height / 2 + mid_num, paintText)
if (ttangle < 270) {
tangle += 2f
postInvalidateDelayed(delayMillis.toLong())
}
}
}
}
}
private fun addLine(i: Int, d: Float, center: Float, center_y: Int, radius: Float, canvas: Canvas?, label_line: Paint, radius_little_circle: Float, little_circle: Paint, line_text: Paint, textSize: Float) {
canvas?.let{
val num = when (showType) {
1 -> "${(values[i].num / total * 100).toInt()}%"
else -> values[i].num.toInt().toString()
}
when (d) {
in 0f..90f -> {
val startX = (center + radius * sin(d * Math.PI / 180)).toFloat()
val startY = (center_y - radius * cos(d * Math.PI / 180)).toFloat()
val stopX = startX + height / 32
val stopY = startY - height / 32
it.drawLine(startX, startY, stopX, stopY, label_line)
it.drawLine(stopX, stopY, stopX + radius, stopY, label_line)
it.drawCircle(stopX + radius + radius_little_circle, stopY, radius_little_circle, little_circle)
it.drawText(values[i].type, stopX + radius / 4, stopY - height / 128f, line_text)
it.drawText("$num", stopX + radius / 3, stopY + textSize / 0.9f, line_text)
}
in 90f..135f -> {
val startX = (center + radius * sin(d * Math.PI / 180)).toFloat()
val startY = (center_y - radius * cos(d * Math.PI / 180)).toFloat()
val stopX = startX + height / 32
val stopY = startY + height / 32
it.drawLine(startX, startY, stopX, stopY, label_line)
it.drawLine(stopX, stopY, stopX + radius, stopY, label_line)
it.drawCircle(stopX + radius + radius_little_circle, stopY, radius_little_circle, little_circle)
it.drawText(values[i].type, stopX + radius / 4, stopY - height / 128f, line_text)
it.drawText("$num", stopX + radius / 3, stopY + textSize / 0.9f, line_text)
}
in 135f..180f -> {
val startX = (center + radius * sin(d * Math.PI / 180)).toFloat()
val startY = (center_y - radius * cos(d * Math.PI / 180)).toFloat()
val stopX = startX + height / 32
val stopY = startY + height / 32
it.drawLine(startX, startY, stopX, stopY, label_line)
it.drawLine(stopX, stopY, stopX + radius + radius / 4, stopY, label_line)
it.drawCircle(stopX + radius + radius_little_circle + radius / 4, stopY, radius_little_circle, little_circle)
it.drawText(values[i].type, stopX + radius / 2, stopY - height / 128f, line_text)
it.drawText("$num", stopX + radius / 2, stopY + textSize / 0.9f, line_text)
}
in 180f..225f -> {
val startX = (center + radius * sin(d * Math.PI / 180)).toFloat()
val startY = (center_y - radius * cos(d * Math.PI / 180)).toFloat()
val stopX = startX - height / 32
val stopY = startY + height / 32
it.drawLine(startX, startY, stopX, stopY, label_line)
it.drawLine(stopX, stopY, stopX - radius - radius / 4, stopY, label_line)
it.drawCircle(stopX - radius - radius_little_circle - radius / 4, stopY, radius_little_circle, little_circle)
it.drawText(values[i].type, stopX - radius - radius / 6, stopY - height / 128f, line_text)
it.drawText("$num", stopX - radius - radius / 6, stopY + textSize / 0.9f, line_text)
}
in 225f..270f -> {
val startX = (center + radius * sin(d * Math.PI / 180)).toFloat()
val startY = (center_y - radius * cos(d * Math.PI / 180)).toFloat()
val stopX = startX - height / 32
val stopY = startY + height / 32
it.drawLine(startX, startY, stopX, stopY, label_line)
it.drawLine(stopX, stopY, stopX - radius, stopY, label_line)
it.drawCircle(stopX - radius - radius_little_circle, stopY, radius_little_circle, little_circle)
it.drawText(values[i].type, stopX - radius + radius / 6, stopY - height / 128f, line_text)
it.drawText("$num", stopX - radius + radius / 3, stopY + textSize / 0.9f, line_text)
}
in 270f..360f -> {
val startX = (center + radius * sin(d * Math.PI / 180)).toFloat()
val startY = (center_y - radius * cos(d * Math.PI / 180)).toFloat()
val stopX = startX - height / 32
val stopY = startY - height / 32
it.drawLine(startX, startY, stopX, stopY, label_line)
it.drawLine(stopX, stopY, stopX - radius, stopY, label_line)
it.drawCircle(stopX - radius - radius_little_circle, stopY, radius_little_circle, little_circle)
it.drawText(values[i].type, stopX - radius + radius / 6, stopY - height / 128f, line_text)
it.drawText("$num", stopX - radius + radius / 3, stopY + textSize / 0.9f, line_text)
}
}
}
}
data class DataBean(
var num : Float,
var color : String,
var type : String
)
}
点击跳转代码片段
以上就是具体实现饼状图方法,如何使用呢?在activity或fragment的布局文件中建立一个FrameLayout布局。
<FrameLayout
android:layout_gravity="center"
android:id="@+id/pie_layout"
android:layout_width="match_parent"
android:layout_height="300dp"/>
在activity和fragment中添加view(组件绑定使用的ViewBinding)。
lateinit var layout : ActivityMainBinding
private val dataBeans = mutableListOf<PieChartWidget.DataBean>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
layout = ActivityMainBinding.inflate(layoutInflater)
setContentView(layout.root)
layout.pieLayout.removeAllViews()
layout.pieLayout.addView(PieChartWidget(this,dataBeans))
}
其中,dataBeans是要显示的数据。如下声明:
dataBeans.clear()
dataBeans.add(PieChartWidget.DataBean((100..1000).random().toFloat(),"${R.color.color_statistics4}","名师"))
dataBeans.add(PieChartWidget.DataBean((100..1000).random().toFloat(),"${R.color.color_statistics1}","研究员"))
dataBeans.add(PieChartWidget.DataBean((100..1000).random().toFloat(),"${R.color.color_statistics2}","特级教师"))
dataBeans.add(PieChartWidget.DataBean((100..1000).random().toFloat(),"${R.color.color_statistics3}","优秀教师"))
dataBeans.add(PieChartWidget.DataBean((100..1000).random().toFloat(),"${R.color.color_statistics5}","高级教师"))
dataBeans.add(PieChartWidget.DataBean((100..1000).random().toFloat(),"${R.color.color_statistics6}","骨干教师"))
第一个参数为你要统计的数据大小;第二个为你的颜色配置文件,在res/values/color.xml文件里进行配置即可;最后一个参数为每个统计参数的名称。以上就可以实现饼状图的显示。
有朋友就会问中间的标题、单位和百分比想要修改怎么修改呢?
PieChartView(this, teacherModel,"教师师资","个",1)
通过以上代码初始化就可以显示标题、单位、百分比了,如图。
以上就是android饼状统计图的使用方法了,具体可以进入我的仓库查看,代码地址。