亲爱的各位小伙伴们,我又回来了。前段时间因为工作比较忙,加上一个生活上的事情,文章就没有更新哦。然后自定义饼状图系列酒夭折了。一部分是因为没时间去写,还有一部分原因是因为感觉写不下去了。自己一开始想的太简单了。才发现想要达到那样的效果实在是有太多的坑要去踩。后期一定尽量慢慢的去完善那个控件的。希望小伙伴们支持。
本文所有内容都是自己一个字一个字手打。代码逻辑部分参考http://blog.csdn.net/crazy__chen/article/details/50163693如有问题请指出。谢谢!
目前最终效果图.png进入正题,先看一下效果
目前可以自定义的属性有以下几点:
- 雷达网的颜色
- 雷达网的分支个数(也就是数据的种类)
- 雷达网的层数(数据的值的分层)
- 每个选项的点的颜色
- 填充区域的颜色
目前待优化有以下几个:
- 填充数据问题
- 文字标识显示问题
- 屏幕适配问题
代码实现
先获取XML文件中的属性。这里有个要注意的问题。也是待优化的地方。XML文件中定义的数据的个数和传入的数据的个数必须要一样,不然会报错。
public RadarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initCifg(context,attrs);
initView();
}
private void initCifg(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RadarView);
textColor = typedArray.getColor(R.styleable.RadarView_textPaintColor, Color.BLACK);
radarColor = typedArray.getColor(R.styleable.RadarView_radarPaintColor, Color.GRAY);
valueColor = typedArray.getColor(R.styleable.RadarView_valuePaintColor, Color.BLUE);
levelCount = typedArray.getInt(R.styleable.RadarView_levelCount, 6);
valueCount = typedArray.getInt(R.styleable.RadarView_valueCount, 6);
pointColor = typedArray.getColor(R.styleable.RadarView_pointColor, Color.BLUE);
typedArray.recycle();
}
/**
* 初始化View参数
*/
private void initView() {
radarPaint = new Paint();
radarPaint.setColor(radarColor);
radarPaint.setStyle(Paint.Style.STROKE);
textPaint = new Paint();
textPaint.setTextSize(40);
textPaint.setColor(Color.BLACK);
datas = new ArrayList<>();
valuePaint = new Paint();
valuePaint.setColor(valueColor);
datas.add(new RadarViewBean("德",10));
datas.add(new RadarViewBean("智",20));
datas.add(new RadarViewBean("体",30));
datas.add(new RadarViewBean("美",40));
datas.add(new RadarViewBean("劳",50));
datas.add(new RadarViewBean("爱",60));
}
看一下attr中的代码
<declare-styleable name="RadarView">
<attr name="radarPaintColor" format="color" />
<attr name="valuePaintColor" format="color" />
<attr name="textPaintColor" format="color" />
<attr name="valueCount" format="integer" />
<attr name="levelCount" format="integer" />
<attr name="pointColor" format="color"/>
</declare-styleable>
确定View显示的位置
雷达图简单来说可以看成是一个圆在界面显示。要保证View在界面上不会有挡住的部分。所以View的宽高要按照空间在XML中的位置的最短的一个属性来设置。
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
radiusMax = Math.min(w, h) / 2 * 0.7f;//这里乘上0.7f也是进一步防止空间被挡住。因为还要给文字表示留位置,所以缩小的稍微多了一点。
centerX = w / 2;
centerY = h / 2;
postInvalidate();
super.onSizeChanged(w, h, oldw, oldh);
}
绘画雷达图背景
- 第一步要绘制雷达图背后的网格图。
以一个点为起点,每隔一定的角度画线。角度是根据值得个数来改变。直到换成一个多边形。
- 外层循环控制网格线绘制到第几层
- 内层循环控制每层网格线的绘制
/**
* 绘制雷达的层层边框
*
* @param canvas
*/
private void drawRadarShape(Canvas canvas) {
Path path = new Path();
float spacing = radiusMax / (count - 1);
for (int i = 1; i < count; i++) {
float currtRadius = spacing * i;
path.reset();
for (int j = 0; j < valueCount; j++) {
if (j == 0) {
path.moveTo(centerX + currtRadius, centerY);
} else {
float x = (float) (centerX + currtRadius * Math.cos(angle * j));
float y = (float) (centerY + currtRadius * Math.sin(angle * j));
path.lineTo(x, y);
}
}
path.close();
canvas.drawPath(path, radarPaint);
}
}
- 第二部要绘制雷达图向外辐射的线
以View的中心点为起点,每隔一定的角度画一条线。这个比较简单。不详细说。
/**
* 画多边形每个对角线的连线
*
* @param canvas
*/
private void drawRadarLines(Canvas canvas) {
Path path = new Path();
for (int i = 0; i < valueCount; i++) {
path.reset();
path.moveTo(centerX, centerY);
float x = (float) (centerX + radiusMax * Math.cos(angle * i));
float y = (float) (centerY + radiusMax * Math.sin(angle * i));
path.lineTo(x, y);
canvas.drawPath(path, radarPaint);
}
}
效果图2.png
绘制显示标识
绘制文字的过程稍微有点复杂。这里参考了较多别人处理文字的方式。
文字在绘制的时候要考虑到不要遮挡View本身,所以需要考虑到文字所在的位置,一般是按象限来看。同时也需要考虑到文字的长度和高度
这里需要说一下的是 Paint.FontMetrics 这个属性。
这个里面需要注意的文字高度有多个高度,这里在计算文字高度的时候需要注意一下。
/**
* 绘制每条线的标识
*
* @param canvas
*/
private void drawRadarText(Canvas canvas) {
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float fontHeight = fontMetrics.descent - fontMetrics.ascent;
for (int i = 0; i < valueCount; i++) {
float x = (float) (centerX + (radiusMax + fontHeight / 2) * Math.cos(angle * i));
float y = (float) (centerY + (radiusMax + fontHeight / 2) * Math.sin(angle * i));
if (angle * i >= 0 && angle * i <= Math.PI / 2) {//第4象限
canvas.drawText(datas.get(i).getTitle(), x, y, textPaint);
} else if (angle * i >= 3 * Math.PI / 2 && angle * i <= Math.PI * 2) {//第3象限
canvas.drawText(datas.get(i).getTitle(), x, y, textPaint);
} else if (angle * i > Math.PI / 2 && angle * i <= Math.PI) {//第2象限
float dis = textPaint.measureText(datas.get(i).getTitle());//文本长度
canvas.drawText(datas.get(i).getTitle(), x - dis, y, textPaint);
} else if (angle * i >= Math.PI && angle * i < 3 * Math.PI / 2) {//第1象限
float dis = textPaint.measureText(datas.get(i).getTitle());//文本长度
canvas.drawText(datas.get(i).getTitle(), x - dis, y, textPaint);
}
}
}
最后绘制填充区域
- 这里需要注意把文字笔的属性调一下
valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
也不是全程都是这个,在画点和线的时候要注意。
/**
* 画填充区域
* @param canvas
*/
private void drawRadarValue(Canvas canvas) {
Path path = new Path();
valuePaint.setAlpha(255); //设置透明度的时候可以自己随意设置
for(int i=0;i<valueCount;i++){
double percent = datas.get(i).getValue()/maxValue;
float x = (float) (centerX+radiusMax*Math.cos(angle*i)*percent);
float y = (float) (centerY+radiusMax*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);
}
总结
这个控件总的来说还是比较简单的,需要注意的基础点比较多。然后在绘制文字表示的时候需要注意的点也比较多。具体使用下来暂时没有出现问题。如果有问题请回复或者私信联系我修改。谢谢!附上源码。
点我跳转
最后希望大家点击一下喜欢!
网友评论