最近业务要做分数雷达图,到网上找了很大,原理都差不多,但是要适用自己的业务,还需要微调。下面是我微调后的效果图
![](https://img.haomeiwen.com/i6273713/bee9bcdcfc21d252.png)
原理很简单
- 1,确定雷达图中心点的坐标
- 2,用正三角sin,反三角函数cos计算五个类别的坐标点
- 3,绘制蜘蛛网
- 4,绘制直线
- 5,绘制覆盖区域(包含红点)
下面结合代码给大家讲解下
-1, 下面是需要用到的一些属性
public class RadarView extends View {
//数据个数
private int count = 5;
//成绩圆点半径
private int valueRadius = 8;
//网格最大半径
private float radius;
//中心X
private float centerX;
//中心Y
private float centerY;
//雷达区画笔
private Paint mainPaint;
//文本画笔
private Paint textPaint;
//数据区画笔
private Paint valuePaint;
//标题文字
private List<String> titles;
//各维度分值
private List<Double> data;
//数据最大值
private double maxValue = 100;
//弧度
private float angle;
}
- 2,获取中心点坐标
这个是在view的onSizeChanged方法里获取当前view的宽高,取宽高的小值,然后再找中心点作为雷达图的中心点。这样做的好处是可以使雷达图自适应
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
radius = Math.min(w, h) / 2 * 0.8f;
centerX = w / 2;
centerY = h / 2;
//一旦size发生改变,重新绘制
postInvalidate();
super.onSizeChanged(w, h, oldw, oldh);
}
- 3,开始绘制
@Override
protected void onDraw(Canvas canvas) {
drawPolygon(canvas);//绘制蜘蛛网
drawLines(canvas);//绘制直线
drawTitle(canvas);//绘制标题
drawRegion(canvas);//绘制覆盖区域
}
下面贴出所有代码,代码里有很详细的注释
package com.example.qcl.demo.radarmap;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* 2018/6/15 11:09
* Created by qcl
* wechat:2501902696
*/
public class RadarView extends View {
//数据个数
private int count = 5;
//成绩圆点半径
private int valueRadius = 8;
//网格最大半径
private float radius;
//中心X
private float centerX;
//中心Y
private float centerY;
//雷达区画笔
private Paint mainPaint;
//文本画笔
private Paint textPaint;
//数据区画笔
private Paint valuePaint;
//标题文字
private List<String> titles;
//各维度分值
private List<Double> data;
//数据最大值
private double maxValue = 100;
//弧度
private float angle;
public RadarView(Context context) {
this(context, null);
}
public RadarView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RadarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//雷达区画笔初始化
mainPaint = new Paint();
mainPaint.setColor(Color.BLACK);
mainPaint.setAntiAlias(true);
mainPaint.setStrokeWidth(1);
mainPaint.setStyle(Paint.Style.STROKE);
//文本画笔初始化
textPaint = new Paint();
textPaint.setColor(Color.BLACK);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(30);
textPaint.setStrokeWidth(1);
textPaint.setAntiAlias(true);
//数据区(分数)画笔初始化
valuePaint = new Paint();
valuePaint.setColor(Color.RED);
valuePaint.setAntiAlias(true);
valuePaint.setStyle(Paint.Style.FILL);
titles = new ArrayList<>();
titles.add("语文");
titles.add("数学");
titles.add("英语");
titles.add("政治");
titles.add("历史");
count = titles.size();
//默认分数
data = new ArrayList<>(count);
data.add(100.0);
data.add(80.0);
data.add(90.0);
data.add(70.0);
data.add(60.0);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
radius = Math.min(w, h) / 2 * 0.8f;
centerX = w / 2;
centerY = h / 2;
//一旦size发生改变,重新绘制
postInvalidate();
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
drawPolygon(canvas);//绘制蜘蛛网
drawLines(canvas);//绘制直线
drawTitle(canvas);//绘制标题
drawRegion(canvas);//绘制覆盖区域
}
/**
* 绘制多边形
*
* @param canvas
*/
private void drawPolygon(Canvas canvas) {
Path path = new Path();
//1度=1*PI/180 360度=2*PI 那么我们每旋转一次的角度为2*PI/内角个数
//中心与相邻两个内角相连的夹角角度
angle = (float) (2 * Math.PI / count);
//每个蛛丝之间的间距
float r = radius / (count - 1);
for (int i = 0; i < count; i++) {
//当前半径
float curR = r * i;
path.reset();
for (int j = 0; j < count; j++) {
if (j == 0) {
float x = (float) (centerX + curR * Math.sin(angle));
float y = (float) (centerY - curR * Math.cos(angle));
path.moveTo(x, y);
} else {
//根据半径,计算出蜘蛛丝上每个点的坐标
float x1 = (float) (centerX + curR * Math.sin(angle / 2));
float y1 = (float) (centerY + curR * Math.cos(angle / 2));
path.lineTo(x1, y1);
float x2 = (float) (centerX - curR * Math.sin(angle / 2));
float y2 = (float) (centerY + curR * Math.cos(angle / 2));
path.lineTo(x2, y2);
float x3 = (float) (centerX - curR * Math.sin(angle));
float y3 = (float) (centerY - curR * Math.cos(angle));
path.lineTo(x3, y3);
float x4 = centerX;
float y4 = centerY - curR;
path.lineTo(x4, y4);
float x = (float) (centerX + curR * Math.sin(angle));
float y = (float) (centerY - curR * Math.cos(angle));
path.lineTo(x, y);
}
}
path.close();
canvas.drawPath(path, mainPaint);
}
}
/**
* 绘制直线
*/
private void drawLines(Canvas canvas) {
Path path = new Path();
path.reset();
//直线1
path.moveTo(centerX, centerY);
float x1 = (float) (centerX + radius * Math.sin(angle));
float y1 = (float) (centerY - radius * Math.cos(angle));
path.lineTo(x1, y1);
//直线2
path.moveTo(centerX, centerY);
float x2 = (float) (centerX + radius * Math.sin(angle / 2));
float y2 = (float) (centerY + radius * Math.cos(angle / 2));
path.lineTo(x2, y2);
//直线3
path.moveTo(centerX, centerY);
float x3 = (float) (centerX - radius * Math.sin(angle / 2));
float y3 = (float) (centerY + radius * Math.cos(angle / 2));
path.lineTo(x3, y3);
//直线4
path.moveTo(centerX, centerY);
float x4 = (float) (centerX - radius * Math.sin(angle));
float y4 = (float) (centerY - radius * Math.cos(angle));
path.lineTo(x4, y4);
//直线5
path.moveTo(centerX, centerY);
float x5 = (float) (centerX);
float y5 = (float) (centerY - radius);
path.lineTo(x5, y5);
path.close();
canvas.drawPath(path, mainPaint);
}
/**
* 绘制标题文字
*
* @param canvas
*/
private void drawTitle(Canvas canvas) {
if (count != titles.size()) {
return;
}
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float fontHeight = fontMetrics.descent - fontMetrics.ascent;//标题高度
//绘制文字1
float x1 = centerX;
float y1 = centerY - radius;
canvas.drawText(titles.get(0), x1, y1 - fontHeight / 5, textPaint);
//绘制文字2
float x2 = (float) (centerX + radius * Math.sin(angle));
float y2 = (float) (centerY - radius * Math.cos(angle));
float dis = textPaint.measureText(titles.get(1));//标题一半的宽度
canvas.drawText(titles.get(1), x2 + dis, y2 + fontHeight / 5, textPaint);
//绘制文字3
float x3 = (float) (centerX + radius * Math.sin(angle / 2));
float y3 = (float) (centerY + radius * Math.cos(angle / 2));
canvas.drawText(titles.get(2), x3, y3 + fontHeight, textPaint);
//绘制文字4
float x4 = (float) (centerX - radius * Math.sin(angle / 2));
float y4 = (float) (centerY + radius * Math.cos(angle / 2));
canvas.drawText(titles.get(3), x4, y4 + fontHeight, textPaint);
//绘制文字5
float x5 = (float) (centerX - radius * Math.sin(angle));
float y5 = (float) (centerY - radius * Math.cos(angle));
float dis5 = textPaint.measureText(titles.get(1));//标题的宽度
canvas.drawText(titles.get(4), x5 - dis5, y5 - fontHeight / 5, textPaint);
}
/**
* 绘制覆盖区域
*/
private void drawRegion(Canvas canvas) {
valuePaint.setAlpha(255);
Path path = new Path();
double dataValue;
double percent;
//绘制圆点1
dataValue = data.get(0);
if (dataValue != maxValue) {
percent = dataValue / maxValue;
} else {
percent = 1;
}
float x1 = centerX;
float y1 = (float) (centerY - radius * percent);
path.moveTo(x1, y1);
canvas.drawCircle(x1, y1, valueRadius, valuePaint);
//绘制圆点2
dataValue = data.get(1);
if (dataValue != maxValue) {
percent = dataValue / maxValue;
} else {
percent = 1;
}
float x2 = (float) (centerX + radius * percent * Math.sin(angle));
float y2 = (float) (centerY - radius * percent * Math.cos(angle));
path.lineTo(x2, y2);
canvas.drawCircle(x2, y2, valueRadius, valuePaint);
//绘制圆点3
dataValue = data.get(2);
if (dataValue != maxValue) {
percent = dataValue / maxValue;
} else {
percent = 1;
}
float x3 = (float) (centerX + radius * percent * Math.sin(angle / 2));
float y3 = (float) (centerY + radius * percent * Math.cos(angle / 2));
path.lineTo(x3, y3);
canvas.drawCircle(x3, y3, valueRadius, valuePaint);
//绘制圆点4
dataValue = data.get(3);
if (dataValue != maxValue) {
percent = dataValue / maxValue;
} else {
percent = 1;
}
float x4 = (float) (centerX - radius * percent * Math.sin(angle / 2));
float y4 = (float) (centerY + radius * percent * Math.cos(angle / 2));
path.lineTo(x4, y4);
canvas.drawCircle(x4, y4, valueRadius, valuePaint);
//绘制圆点5
dataValue = data.get(3);
if (dataValue != maxValue) {
percent = dataValue / maxValue;
} else {
percent = 1;
}
float x5 = (float) (centerX - radius * percent * Math.sin(angle));
float y5 = (float) (centerY - radius * percent * Math.cos(angle));
path.lineTo(x5, y5);
canvas.drawCircle(x5, y5, valueRadius, valuePaint);
path.close();
valuePaint.setStyle(Paint.Style.STROKE);
//绘制覆盖区域外的连线
canvas.drawPath(path, valuePaint);
//填充覆盖区域
valuePaint.setAlpha(128);
valuePaint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, valuePaint);
}
//设置蜘蛛网颜色
public void setMainPaint(Paint mainPaint) {
this.mainPaint = mainPaint;
postInvalidate();
}
//设置标题颜色
public void setTextPaint(Paint textPaint) {
this.textPaint = textPaint;
}
//设置覆盖局域颜色
public void setValuePaint(Paint valuePaint) {
this.valuePaint = valuePaint;
postInvalidate();
}
//设置各门得分
public void setData(List<Double> data) {
this.data = data;
postInvalidate();
}
//设置满分分数,默认是100分满分
public void setMaxValue(float maxValue) {
this.maxValue = maxValue;
}
}
使用
- 1,在布局文件中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey666"
android:orientation="vertical">
<com.example.qcl.demo.radarmap.RadarView
android:id="@+id/radarview"
android:layout_width="250dp"
android:layout_height="200dp"
android:background="@color/colorWhite"/>
<com.example.qcl.demo.radarmap.RadarView
android:layout_width="250dp"
android:layout_height="200dp"
android:layout_marginTop="30dp"
android:background="@color/colorWhite"/>
</LinearLayout>
在activity中
public class RadarMapActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_radar_map);
RadarView radarview = findViewById(R.id.radarview);
List<Double> datas = new ArrayList<>();
datas.add(100.0);//语文100分
datas.add(80.0);//数学80分
datas.add(90.0);//英语90分
datas.add(70.0);//政治70分
datas.add(60.0);//历史60分
radarview.setData(datas);
}
}
显示样式
![](https://img.haomeiwen.com/i6273713/59bcf2e127e865be.png)
网友评论