先看效果图:
使用方式
使用方式很简单,只要在布局文件里面,直接使用这个控件就好了,记得给它设置一个合适的具体的大小。
另外可以控制绘制的是多少边形,通过提供的一些public方法,可以设置画笔颜色等
/**
* @param titles
*/
public void setTitles(String[] titles) {
this.titles = titles;
}
/**
* 各维度分值
* @param data data
*/
public void setData(double[] data) {
this.data = data;
}
/**
* 数据最大值
* @param maxValue maxValue
*/
public void setMaxValue(float maxValue) {
this.maxValue = maxValue;
}
/**
* 设置蜘蛛网颜色
*
* @param color
*/
public void setMainPaintColor(int color) {
mMainPaint.setColor(color);
}
/**
* 设置标题颜色
*
* @param color
*/
public void setTextPaintColor(int color) {
mTextPaint.setColor(color);
}
/**
* @param color
*/
public void setValuePaintColor(int color) {
mValuePaint.setColor(color);
}
具体实现
1、获得布局中心
我们在onSizeChanged(int w, int h, int oldw, int oldh)方法里面,根据View的长宽,获取整个布局的中心坐标,因为整个雷达都是以整个中心开始绘制的。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//网格最大半径
radius = (float) Math.min(h, w) / 2 * 0.9f;
centerX = w / 2;
centerY = h / 2;
postInvalidate();
}
2、绘制蜘蛛网络
/**
* 绘制正多边形
*/
private void drawPolygon(Canvas canvas) {
Path path = new Path();
//蜘蛛丝之间的间距
float r = radius / count;
for (int i = 1; i <= count; i++) {
//当前半径
float curR = r * i;
path.reset();
for (int j = 0; j < count; j++) {
if (j == 0) {
path.moveTo(centerX + curR, centerY);
} else {
//根据半径,计算出蜘蛛丝上每个点的坐标
float x = (float) (centerX + curR * Math.cos(angle * j));
float y = (float) (centerY + curR * Math.sin(angle * j));
path.lineTo(x, y);
}
}
//闭合路径
path.close();
canvas.drawPath(path, mMainPaint);
}
}
3、绘制从中心到末端的直线
同样根据半径,计算出每个末端坐标
/**
* 绘制从中心到末端的直线
*/
private void drawLines(Canvas canvas) {
Path path = new Path();
for (int i = 0; i < count; i++) {
path.reset();
path.moveTo(centerX, centerY);
//计算最外侧蜘蛛丝上每个点的坐标
float x = (float) (centerX + radius * Math.cos(angle * i));
float y = (float) (centerY + radius * Math.sin(angle * i));
path.lineTo(x, y);
canvas.drawPath(path, mMainPaint);
}
}
4、绘制文本
对于文本的绘制,首先要找到末端的坐标,由于末端和文本有一定距离,给每个末端加上这个距离以后,再绘制文本。
另外,当文本在左边时,由于不希望文本和蜘蛛网交叉,我们可以先计算出文本的长度,然后使起始绘制坐标向左偏移这个长度。
/**
* 绘制文本
* 先计算出文本的长度,然后使起始绘制坐标向左偏移这个长度。
*/
private void drawText(Canvas canvas) {
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
float fontHeight = fontMetrics.descent - fontMetrics.ascent;
for (int i = 0; i < count; i++) {
//计算最外侧蜘蛛丝上每个点的坐标
float x = (float) (centerX + (radius + fontHeight / 2) * Math.cos(angle * i));
float y = (float) (centerY + (radius + fontHeight / 2) * Math.sin(angle * i));
//第4象限
if (angle * i >= 0 && angle * i <= Math.PI / 2) {
canvas.drawText(titles[i], x, y, mTextPaint);
}
//第3象限
else if (angle * i >= Math.PI / 2 && angle * i <= Math.PI) {
float dis = mTextPaint.measureText(titles[i]);
canvas.drawText(titles[i], x - dis, y + dis, mTextPaint);
}
//第2象限
else if (angle * i >= Math.PI && angle * i <= 3 * Math.PI / 2) {
canvas.drawText(titles[i], x, y, mTextPaint);
}
//第1象限
else if (angle * i >= 3 * Math.PI / 2 && angle * i <= Math.PI * 2) {
canvas.drawText(titles[i], x, y, mTextPaint);
}
}
}
5、绘制覆盖区域
覆盖区域,只要使用path记录下坐标点,然后设
valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
使path包围区域被填充
/**
* 绘制区域
*/
private void drawRegion(Canvas canvas) {
Path path = new Path();
mValuePaint.setAlpha(255);
for (int i = 0; i < count; i++) {
double percent = data[i] / maxValue;
//计算最外侧蜘蛛丝上每个点的坐标
float x = (float) (centerX + radius * Math.cos(angle * i) * percent);
float y = (float) (centerY + radius * Math.sin(angle * i) * percent);
if (i == 0) {
path.moveTo(x, centerY);
} else {
path.lineTo(x, y);
}
//绘制小圆点
canvas.drawCircle(x, y, 20, mValuePaint);
}
mValuePaint.setAlpha(127);
//绘制填充区域
canvas.drawPath(path, mValuePaint);
}
完整代码如下:
/**
* @desciption: 雷达
*/
public class RadarView extends View {
/**
* 多边形点的个数
*/
private int count = 6;
/**
* 多边形均等分角度
*/
private float angle = (float) (Math.PI * 2 / count);
/**
* 网格最大半径
*/
private float radius;
/**
* 中心x
*/
private int centerX;
/**
* 中心y
*/
private int centerY;
/**
* 数据最大值
*/
private float maxValue = 100;
/**
* 各维度分值
*/
private double[] data = {100, 60, 60, 60, 100, 50, 10, 20};
private String[] titles = {"a", "b", "c", "d", "e", "f"};
/**
* 雷达区画笔
*/
private Paint mMainPaint;
/**
* 文本画笔
*/
private Paint mTextPaint;
/**
* 数据区画笔
*/
private Paint mValuePaint;
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);
initPaint();
}
private void initPaint() {
mMainPaint = new Paint();
mMainPaint.setAntiAlias(true);
mMainPaint.setStrokeWidth(3);
mMainPaint.setStyle(Paint.Style.STROKE);
mMainPaint.setColor(Color.BLACK);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(Color.BLACK);
mTextPaint.setTextSize(60);
mValuePaint = new Paint();
mValuePaint.setAntiAlias(true);
mValuePaint.setColor(Color.BLUE);
mValuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//网格最大半径
radius = (float) Math.min(h, w) / 2 * 0.9f;
centerX = w / 2;
centerY = h / 2;
postInvalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制正多边形
drawPolygon(canvas);
//绘制从中心到末端的直线
drawLines(canvas);
//绘制文本
drawText(canvas);
//绘制区域
drawRegion(canvas);
}
/**
* 绘制正多边形
*/
private void drawPolygon(Canvas canvas) {
Path path = new Path();
//蜘蛛丝之间的间距
float r = radius / count;
for (int i = 1; i <= count; i++) {
//当前半径
float curR = r * i;
path.reset();
for (int j = 0; j < count; j++) {
if (j == 0) {
path.moveTo(centerX + curR, centerY);
} else {
//根据半径,计算出蜘蛛丝上每个点的坐标
float x = (float) (centerX + curR * Math.cos(angle * j));
float y = (float) (centerY + curR * Math.sin(angle * j));
path.lineTo(x, y);
}
}
//闭合路径
path.close();
canvas.drawPath(path, mMainPaint);
}
}
/**
* 绘制从中心到末端的直线
*/
private void drawLines(Canvas canvas) {
Path path = new Path();
for (int i = 0; i < count; i++) {
path.reset();
path.moveTo(centerX, centerY);
//计算最外侧蜘蛛丝上每个点的坐标
float x = (float) (centerX + radius * Math.cos(angle * i));
float y = (float) (centerY + radius * Math.sin(angle * i));
path.lineTo(x, y);
canvas.drawPath(path, mMainPaint);
}
}
/**
* 绘制文本
* 先计算出文本的长度,然后使起始绘制坐标向左偏移这个长度。
*/
private void drawText(Canvas canvas) {
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
float fontHeight = fontMetrics.descent - fontMetrics.ascent;
for (int i = 0; i < count; i++) {
//计算最外侧蜘蛛丝上每个点的坐标
float x = (float) (centerX + (radius + fontHeight / 2) * Math.cos(angle * i));
float y = (float) (centerY + (radius + fontHeight / 2) * Math.sin(angle * i));
//第4象限
if (angle * i >= 0 && angle * i <= Math.PI / 2) {
canvas.drawText(titles[i], x, y, mTextPaint);
}
//第3象限
else if (angle * i >= Math.PI / 2 && angle * i <= Math.PI) {
float dis = mTextPaint.measureText(titles[i]);
canvas.drawText(titles[i], x - dis, y + dis, mTextPaint);
}
//第2象限
else if (angle * i >= Math.PI && angle * i <= 3 * Math.PI / 2) {
canvas.drawText(titles[i], x, y, mTextPaint);
}
//第1象限
else if (angle * i >= 3 * Math.PI / 2 && angle * i <= Math.PI * 2) {
canvas.drawText(titles[i], x, y, mTextPaint);
}
}
}
/**
* 绘制区域
*/
private void drawRegion(Canvas canvas) {
Path path = new Path();
mValuePaint.setAlpha(255);
for (int i = 0; i < count; i++) {
double percent = data[i] / maxValue;
//计算最外侧蜘蛛丝上每个点的坐标
float x = (float) (centerX + radius * Math.cos(angle * i) * percent);
float y = (float) (centerY + radius * Math.sin(angle * i) * percent);
if (i == 0) {
path.moveTo(x, centerY);
} else {
path.lineTo(x, y);
}
//绘制小圆点
canvas.drawCircle(x, y, 20, mValuePaint);
}
mValuePaint.setAlpha(127);
//绘制填充区域
canvas.drawPath(path, mValuePaint);
}
/**
* @param titles
*/
public void setTitles(String[] titles) {
this.titles = titles;
}
/**
* 各维度分值
*
* @param data data
*/
public void setData(double[] data) {
this.data = data;
}
/**
* 数据最大值
*
* @param maxValue maxValue
*/
public void setMaxValue(float maxValue) {
this.maxValue = maxValue;
}
/**
* 设置蜘蛛网颜色
*
* @param color
*/
public void setMainPaintColor(int color) {
mMainPaint.setColor(color);
}
/**
* 设置标题颜色
*
* @param color
*/
public void setTextPaintColor(int color) {
mTextPaint.setColor(color);
}
/**
* @param color
*/
public void setValuePaintColor(int color) {
mValuePaint.setColor(color);
}
}