美文网首页
Android雷达图View

Android雷达图View

作者: 撸代码的周先生 | 来源:发表于2017-09-14 16:52 被阅读0次

    亲爱的各位小伙伴们,我又回来了。前段时间因为工作比较忙,加上一个生活上的事情,文章就没有更新哦。然后自定义饼状图系列酒夭折了。一部分是因为没时间去写,还有一部分原因是因为感觉写不下去了。自己一开始想的太简单了。才发现想要达到那样的效果实在是有太多的坑要去踩。后期一定尽量慢慢的去完善那个控件的。希望小伙伴们支持。

    本文所有内容都是自己一个字一个字手打。代码逻辑部分参考http://blog.csdn.net/crazy__chen/article/details/50163693如有问题请指出。谢谢!

    进入正题,先看一下效果

    目前最终效果图.png
    目前可以自定义的属性有以下几点:
    1. 雷达网的颜色
    2. 雷达网的分支个数(也就是数据的种类)
    3. 雷达网的层数(数据的值的分层)
    4. 每个选项的点的颜色
    5. 填充区域的颜色
    目前待优化有以下几个:
    1. 填充数据问题
    2. 文字标识显示问题
    3. 屏幕适配问题

    代码实现

    先获取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);
        }
    

    绘画雷达图背景

    1. 第一步要绘制雷达图背后的网格图。
      以一个点为起点,每隔一定的角度画线。角度是根据值得个数来改变。直到换成一个多边形。
    • 外层循环控制网格线绘制到第几层
    • 内层循环控制每层网格线的绘制
     /**
         * 绘制雷达的层层边框
         *
         * @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);
            }
        }
    
    1. 第二部要绘制雷达图向外辐射的线
      以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);
        }
    
    

    总结

    这个控件总的来说还是比较简单的,需要注意的基础点比较多。然后在绘制文字表示的时候需要注意的点也比较多。具体使用下来暂时没有出现问题。如果有问题请回复或者私信联系我修改。谢谢!附上源码。
    点我跳转
    最后希望大家点击一下喜欢!

    相关文章

      网友评论

          本文标题:Android雷达图View

          本文链接:https://www.haomeiwen.com/subject/jpjdsxtx.html