简介
最近因为项目需求,要实现一款雷达图来表示用户的各种成就值
雷达图的绘制很简单,只要思路清晰按部就班的绘制就可以了,其中使用得最多,是路径path类的使用,使用这个类可以让我们更加方便地绘制出正多边形等效果。
效果图如下:
使用方式
使用方式很简单,只要在布局文件里面,直接使用这个控件就好了,记得给它设置一个合适的具体的大小。
另外可以控制绘制的是多少边形,通过提供的一些public方法,可以设置画笔颜色等。
当然大家也可以按照自己的需求去修改啦。
//设置标题 public void setTitles(String[] titles) { this.titles = titles; } //设置数值 public void setData(double[] data) { this.data = data; } //设置最大数值 public void setMaxValue(float maxValue) { this.maxValue = maxValue; } //设置蜘蛛网颜色 public void setMainPaintColor(int color){ mainPaint.setColor(color); } //设置标题颜色 public void setTextPaintColor(int color){ textPaint.setColor(color); } //设置覆盖局域颜色 public void setValuePaintColor(int color){ valuePaint.setColor(color); }
具体实现
1、获得布局中心
我们在onSizeChanged(int w, int h, int oldw, int oldh)方法里面,根据View的长宽,获取整个布局的中心坐标,因为整个雷达都是以整个中心开始绘制的。
public class RadarView extends View { private int count = 6; //数据个数 private float angle = (float) (Math.PI*2/count); private float radius; //网格最大半径 private int centerX; //中心X private int centerY; //中心Y private String[] titles = {"a","b","c","d","e","f"}; private double[] data = {100,60,60,60,100,50,10,20}; //各维度分值 private float maxValue = 100; //数据最大值 private Paint mainPaint; //雷达区画笔 private Paint valuePaint; //数据区画笔 private Paint textPaint; //文本画笔 ... @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { radius = Math.min(h, w)/2*0.9f; //中心坐标 centerX = w/2; centerY = h/2; postInvalidate(); super.onSizeChanged(w, h, oldw, oldh); } ...}
2、绘制蜘蛛网络
/** * 绘制正多边形 */ private void drawPolygon(Canvas canvas){ Path path = new Path(); float r = radius/(count-1);//r是蜘蛛丝之间的间距 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, mainPaint); } }
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, mainPaint); } }
4、绘制文本
对于文本的绘制,首先要找到末端的坐标,由于末端和文本有一定距离,给每个末端加上这个距离以后,再绘制文本。
另外,当文本在左边时,由于不希望文本和蜘蛛网交叉,我们可以先计算出文本的长度,然后使起始绘制坐标向左偏移这个长度。
/** * 绘制文字 * @param canvas */ private void drawText(Canvas canvas){ Paint.FontMetrics fontMetrics = textPaint.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)); if(angle*i>=0&&angle*i<=Math.PI/2){//第4象限 canvas.drawText(titles[i], x,y,textPaint); }else if(angle*i>=3*Math.PI/2&&angle*i<=Math.PI*2){//第3象限 canvas.drawText(titles[i], x,y,textPaint); }else if(angle*i>Math.PI/2&&angle*i<=Math.PI){//第2象限 float dis = textPaint.measureText(titles[i]);//文本长度 canvas.drawText(titles[i], x-dis,y,textPaint); }else if(angle*i>=Math.PI&&angle*i<3*Math.PI/2){//第1象限 float dis = textPaint.measureText(titles[i]);//文本长度 canvas.drawText(titles[i], x-dis,y,textPaint); } } }
5、绘制覆盖区域
覆盖区域,只要使用path记录下坐标点,然后设
valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
使path包围区域被填充
/** * 绘制区域 * @param canvas */ private void drawRegion(Canvas canvas){ Path path = new Path(); valuePaint.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,10,valuePaint); } valuePaint.setStyle(Paint.Style.STROKE); canvas.drawPath(path, valuePaint); valuePaint.setAlpha(127); //绘制填充区域 valuePaint.setStyle(Paint.Style.FILL_AND_STROKE); canvas.drawPath(path, valuePaint); }
写在最后
控件很简单,有需要的朋友可以下载源码直接使用或者修改
本篇文章主要是path类的使用
另外这个控件没有做较好的屏幕适配,大家可以根据自己的需要修改
源码下载