自定义雷达图
参考:
http://blog.csdn.net/crazy__chen/article/details/50163693
http://blog.csdn.net/lehtoon_1992/article/details/51036409
http://blog.csdn.net/wangchangshuai0010/article/details/7336435 Path相关
http://blog.csdn.net/sirnuo/article/details/21165665 Paint.drawText()相关
效果图
思路
得到中心点,然后根据角度和半径计算出各个坐标点,使用Path画线,drawText画文本
代码
public class RadarChartView extends View {
private int count = 5; //数据个数
private float angle = (float) (Math.PI * 2 / count); //多边形的角度
private float radius; //多边形最大半径
private int centerX; //中心X
private int centerY; //中心Y
private double[] data = {2,3,1,5,2}; //各维度的值
private String[] dataValue = {"攻击", "速度", "防御", "血量", "魔法"}; //各属性名称
private float maxValue = 5; //数据最大值
private Paint mainPaint; //边线画的笔
private Paint valuePaint; //中心点到数据点的画笔
private Paint textPaint; //文字画笔
public RadarChartView(Context context) {
super(context);
init();
}
public RadarChartView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
/**
* 初始化画笔工具
*/
private void init() {
mainPaint = new Paint();
mainPaint.setColor(Color.parseColor("#000000"));
mainPaint.setStrokeWidth(1);
mainPaint.setAntiAlias(true);
mainPaint.setStyle(Paint.Style.STROKE);
valuePaint = new Paint();
valuePaint.setColor(Color.parseColor("#000000"));
valuePaint.setStrokeWidth(1);
valuePaint.setAntiAlias(true);
valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
textPaint = new Paint();
textPaint.setColor(Color.parseColor("#000000"));
textPaint.setStrokeWidth(1); // 设置空心线宽
textPaint.setAntiAlias(true); // 设置抗锯齿
textPaint.setStyle(Paint.Style.STROKE);
}
/**
* 根据View的长宽,获取整个布局的中心坐标
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w / 2;
centerY = h / 2;
radius = Math.min(w,h) / 2;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawLines(canvas);
drawRegion(canvas);
}
/**
* 绘制从中心到末端的直线,同样根据半径,计算出每个末端坐标
*/
private void drawLines(Canvas canvas) {
Path path = new Path();
path.reset();
float r = radius / (count + 2);//r是蜘蛛丝之间的间距
float curR = (float) (r * data[0]);//当前半径
float x = (float) (centerX + curR * Math.sin(angle));
float y = (float) (centerY - curR * Math.cos(angle));
path.moveTo(x, y);
path.lineTo(centerX, centerY);
curR = (float) (r * data[1]);//当前半径
float x1 = (float) (centerX + curR * Math.sin(angle / 2));
float y1 = (float) (centerY + curR * Math.cos(angle / 2));
path.moveTo(x1, y1);
path.lineTo(centerX, centerY);
curR = (float) (r * data[2]);//当前半径
float x2 = (float) (centerX - curR * Math.sin(angle / 2));
float y2 = (float) (centerY + curR * Math.cos(angle / 2));
path.moveTo(x2, y2);
path.lineTo(centerX, centerY);
curR = (float) (r * data[3]);//当前半径
float x3 = (float) (centerX - curR * Math.sin(angle));
float y3 = (float) (centerY - curR * Math.cos(angle));
path.moveTo(x3, y3);
path.lineTo(centerX, centerY);
curR = (float) (r * data[4]);//当前半径
float x4 = (float) (centerX);
float y4 = (float) (centerY - curR);
path.moveTo(x4, y4);
path.lineTo(centerX, centerY);
path.close();
canvas.drawPath(path, mainPaint);
}
/**
* 绘制覆盖区域,覆盖区域,只要使用path记录下坐标点,然后设
*
* valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);使path包围区域被填充
* @param canvas
*/
private void drawRegion(Canvas canvas) {
Path path = new Path();
valuePaint.setAlpha(255);
float pointR = 4;// 小圆点半径
float r = radius / (count + 2);//r是蜘蛛丝之间的间距
double percent1 = getPercent(data[0], maxValue);
float x1 = (float) (centerX + r * percent1 * Math.sin(angle));
float y1 = (float) (centerY - r * percent1 * Math.cos(angle));
//绘制小圆点
path.moveTo(x1, y1);// 移动画笔
canvas.drawCircle(x1, y1, pointR, valuePaint); // 画圆点
drawText(canvas,dataValue[0],x1,y1,0); // 画文本
double percent2 = getPercent(data[1], maxValue);
float x2 = (float) (centerX + r * percent2 * Math.sin(angle / 2));
float y2 = (float) (centerY + r * percent2 * Math.cos(angle / 2));
//绘制小圆点
path.lineTo(x2, y2); // 画线
canvas.drawCircle(x2, y2, pointR, valuePaint);
drawText(canvas,dataValue[1],x2,y2,1);
double percent3 = getPercent(data[2], maxValue);
float x3 = (float) (centerX - r * percent3 * Math.sin(angle / 2));
float y3 = (float) (centerY + r * percent3 * Math.cos(angle / 2));
//绘制小圆点
path.lineTo(x3, y3);
canvas.drawCircle(x3, y3, pointR, valuePaint);
drawText(canvas,dataValue[2],x3,y3,2);
double percent4 = getPercent(data[3], maxValue);
float x4 = (float) (centerX - r * percent4 * Math.sin(angle));
float y4 = (float) (centerY - r * percent4 * Math.cos(angle));
//绘制小圆点
path.lineTo(x4, y4);
canvas.drawCircle(x4, y4, pointR, valuePaint);
drawText(canvas,dataValue[3],x4,y4,3);
double percent5 = getPercent(data[4], maxValue);
float x5 = (float) (centerX);
float y5 = (float) (centerY - r * percent5);
//绘制小圆点
path.lineTo(x5, y5);
canvas.drawCircle(x5, y5, pointR, valuePaint);
drawText(canvas,dataValue[4],x5,y5,4);
path.lineTo(x1, y1);
path.close();
// 链接各个顶点
valuePaint.setStyle(Paint.Style.STROKE);// 设置镂空
canvas.drawPath(path, valuePaint);
//绘制填充区域
// valuePaint.setAlpha(90);
// valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
// canvas.drawPath(path, valuePaint);
}
/**
* 对数据校验
* @param data
* @param maxValue
* @return
*/
private static double getPercent(double data, float maxValue) {
double percent1;
if (data != maxValue) {
percent1 = data % maxValue;
} else {
percent1 = maxValue;
}
return percent1;
}
/**
* 绘制文本
*/
private void drawText(Canvas canvas , String text, float x,float y,int index){
float textSize = radius / (count + 6);
switch (index){
case 0:
x += textSize*2;
break;
case 1:
x += textSize*2;
y += textSize/2;
break;
case 2:
y += textSize*2;
break;
case 3:
x -= textSize*2;
break;
case 4:
y -= textSize;
break;
}
textPaint.setTextSize(textSize);
textPaint.setTextAlign(Paint.Align.CENTER);// x代表文字中心
canvas.drawText(text,x,y,textPaint);
}
/**
* 设置各个维度的值范围[0,5] , 数组长度5
*
* 分别对应{"态度", "设施", "专业", "效率", "环境"}
* @param data
*/
public void setData(double[] data) {
if (data.length != 5){
throw new IllegalArgumentException("传入数组长度必须是 : " + count);
}
this.data = data;
}