开发文章

Android自定义View -- 蜘蛛网图

经常会在游戏中看到用蜘蛛网图表达用户在游戏中的各种表现,感觉还蛮直观蛮有趣的,刚好最近也在学习自定义View的相关知识,就自己做一个蜘蛛网图,作为笔记了。

先放一张我感觉对自己挺有帮助,从网上找的以这个自定义View流程的图片

View流程的图片.jpg

View最终效果

View最终效果.png

我们可以根据我们最终的效果来分析我们应该如何怎么完成这个View。

首先可以看到最终的View是以整个布局的中心作为坐标原点开始绘制的,所以先要确定布局中心,然后最基础的是有几个共同中心点的正六边形,再将每个六边形的顶点与中心点相连,根据数据绘制需要覆盖的区域,最后在每个顶点旁标注文字。

整体的流程就是这样了,可以开始写代码了

定义变量和初始化

复制内容到剪贴板
  1. private int count = 6;           //数据个数  
  2.   private float angle = (float) (Math.PI * 2 / count);  
  3.   private float radius;            //网格最大半径  
  4.   private int centerX;             //中心X  
  5.   private int centerY;             //中心Y  
  6.   private String[] titles = {"a""b""c""d""e""f"};                          //默认数据  
  7.   private double[] data = {100606060100501020};                       //默认分值  
  8.   private float maxValue = 100;    //数据最大值  
  9.   private Paint mainPaint;         //雷达区画笔  
  10.   private Paint valuePaint;        //数据区画笔  
  11.   private Paint textPaint;         //文本画笔  
复制内容到剪贴板
  1. private void init() {  
  2.        count = Math.min(data.length, titles.length);  
  3.   
  4.        mainPaint = new Paint();  
  5.        mainPaint.setAntiAlias(true);  
  6.        mainPaint.setColor(Color.GRAY);  
  7.        mainPaint.setStyle(Paint.Style.STROKE);  
  8.   
  9.        valuePaint = new Paint();  
  10.        valuePaint.setAntiAlias(true);  
  11.        valuePaint.setColor(Color.BLUE);  
  12.        valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  13.   
  14.        textPaint = new Paint();  
  15.        textPaint.setTextSize(40);  
  16.        textPaint.setStyle(Paint.Style.FILL);  
  17.        textPaint.setColor(Color.BLACK);  
  18.    }  

确定布局中心

onSizeChanged(int w, int h, int oldw, int oldh)方法里面,根据View的长宽,获取整个布局的中心坐标

复制内容到剪贴板
  1. @Override  
  2.   protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  3.       radius = Math.min(h, w) / 2 * 0.9f;  
  4.       centerX = w / 2;  
  5.       centerY = h / 2;  
  6.       postInvalidate();  
  7.       super.onSizeChanged(w, h, oldw, oldh);  
  8.   }  

绘制正六边形

复制内容到剪贴板
  1. private void drawPolygon(Canvas canvas) {  
  2.       Path path = new Path();  
  3.       float r = radius / (count - 1);  
  4.       for (int i = 1; i < count; i++) {  
  5.           float curR = r * i;  
  6.           path.reset();  
  7.           for (int j = 0; j < count; j++) {  
  8.               if (j == 0) {  
  9.                   path.moveTo(centerX + curR, centerY);  
  10.               } else {  
  11.                   float x = (float) (centerX + curR * Math.cos(angle * j));  
  12.                   float y = (float) (centerY + curR * Math.sin(angle * j));  
  13.                   path.lineTo(x, y);  
  14.               }  
  15.           }  
  16.           path.close();  
  17.           canvas.drawPath(path, mainPaint);  
  18.       }  
  19.   }  

绘制正六边形.png

 

绘制顶点到中心直线

复制内容到剪贴板
  1. private void drawLines(Canvas canvas) {  
  2.        Path path = new Path();  
  3.        for (int i = 0; i < count; i++) {  
  4.            path.reset();  
  5.            path.moveTo(centerX, centerY);  
  6.            float x = (float) (centerX + radius * Math.cos(angle * i));  
  7.            float y = (float) (centerY + radius * Math.sin(angle * i));  
  8.            path.lineTo(x, y);  
  9.            canvas.drawPath(path, mainPaint);  
  10.        }  
  11.    }  

绘制顶点到中心直线.png

 

绘制覆盖区域

复制内容到剪贴板
  1. private void drawRegion(Canvas canvas) {  
  2.       Path path = new Path();  
  3.       valuePaint.setAlpha(255);  
  4.       for (int i = 0; i < count; i++) {  
  5.           double percent = data[i] / maxValue;  
  6.           float x = (float) (centerX + radius * Math.cos(angle * i) * percent);  
  7.           float y = (float) (centerY + radius * Math.sin(angle * i) * percent);  
  8.           if (i == 0) {  
  9.               path.moveTo(x, centerY);  
  10.           } else {  
  11.               path.lineTo(x, y);  
  12.           }  
  13.           //绘制小圆点  
  14.           canvas.drawCircle(x, y, 10, valuePaint);  
  15.       }  
  16.       valuePaint.setStyle(Paint.Style.STROKE);  
  17.       canvas.drawPath(path, valuePaint);  
  18.       valuePaint.setAlpha(127);  
  19.       //绘制填充区域  
  20.       valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);  
  21.       canvas.drawPath(path, valuePaint);  
  22.   }  

 

绘制覆盖区域.png

 

绘制文本

对于文本的绘制,首先要找到末端的坐标,由于末端和文本有一定距离,给每个末端加上这个距离以后,再绘制文本。 另外,当文本在左边时,由于不希望文本和蜘蛛网交叉,我们可以先计算出文本的长度,然后使起始绘制坐标向左偏移这个长度。

复制内容到剪贴板
  1. private void drawText(Canvas canvas) {  
  2.        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();  
  3.        float fontHeight = fontMetrics.descent - fontMetrics.ascent;  
  4.        for (int i = 0; i < count; i++) {  
  5.            float x = (float) (centerX + (radius + fontHeight / 2) * Math.cos(angle * i));  
  6.            float y = (float) (centerY + (radius + fontHeight / 2) * Math.sin(angle * i));  
  7.            if (angle * i >= 0 && angle * i <= Math.PI / 2) {  
  8.                canvas.drawText(titles[i], x, y, textPaint);  
  9.            } else if (angle * i >= 3 * Math.PI / 2 && angle * i <= Math.PI * 2) {  
  10.                canvas.drawText(titles[i], x, y, textPaint);  
  11.            } else if (angle * i > Math.PI / 2 && angle * i <= Math.PI) {  
  12.                float dis = textPaint.measureText(titles[i]);  
  13.                canvas.drawText(titles[i], x - dis, y, textPaint);  
  14.            } else if (angle * i >= Math.PI && angle * i < 3 * Math.PI / 2) {  
  15.                float dis = textPaint.measureText(titles[i]);  
  16.                canvas.drawText(titles[i], x - dis, y, textPaint);  
  17.            }  
  18.        }  
  19.    }  

绘制文本.png

到这里就完成了蜘蛛网图的自定义View绘制。

感谢 ThoughtCoding-David 支持 磐实编程网 原文地址:
blog.csdn.net/liuweihhhh/article/details/59539769

文章信息

发布时间:2017-03-03

作者:ThoughtCoding-David

发布者:aquwcw

浏览次数: