自定义雷达图(CustomRadarCharView)

模仿掌盟能力值分析效果

自定义雷达图(CustomRadarCharView)

引言:

今天是我电校庆日,甚是开心,首先要祝我电生日快乐!今天还好没赖床(深夜看片真是一切罪恶的源泉),一大早就跑来图书馆,立个flag今天一定要把这篇文章写完!前两天校运会,今天校庆,所以国庆七天煎熬还没开始,又放了三天假,真的快要无聊死了……还好前天晚上简书上看见一个大神写了一篇文章
[自定义View模仿掌盟能力值分析效果][1]
小伙伴们可以去看看,博主写的很不错,也给了我很大启发,所以我昨天下午开始就写了一个功能更加全面的自定义雷达图,可以说是那位博主文章的加强版,因为我的文章很大程度上借鉴了博主的文章,这里特别感谢!

效果图:

先贴几张图,通过我的 CustomRadarChartView,你可以这样

《自定义雷达图(CustomRadarCharView)》 这里写图片描述

也可以这样

《自定义雷达图(CustomRadarCharView)》 这里写图片描述

还可以这样

《自定义雷达图(CustomRadarCharView)》 这里写图片描述

来张动态的吧!

《自定义雷达图(CustomRadarCharView)》 这里写图片描述

所有的颜色、多边形边数和层数都可以由你自己定制,每个顶点上的字也是根据你传进去的string array自动生成的,不需要你摆弄TextView。

使用:

按照惯例,还是先说一下如何使用,你可以去gitHub上下载我的项目,README上有详细的说明(文章后面其实也有),如果在使用中发现了任何问题,欢迎和我交流

GitHub地址:自定义雷达图(模仿掌盟能力值分析效果 )
微博:蝎子莱莱的微博

如果你对我是如何实现的有兴趣,那就请接着往下看

基础知识

要实现自定义view还是有许多知识需要你了解的,下面是一个小小的总结,如果你对于这些点不是很了解,建议先学习一下,不需要精通,否则你可能看不懂后面的内容。

  • 自定义view的基础知识
  • canvas相关
  • canvas中path的使用(尤其是drawLine、drawText和drawPath)

实现:

先来一张图

《自定义雷达图(CustomRadarCharView)》 这里写图片描述

请不要吐槽这张图像我一样丑,hh,我不知道有些微博那些说明图是用什么画出来的,如果你知道的话麻烦在评价处告诉我,小弟不胜感激!
话说回来,图虽丑但是能把控件是如何实现的讲个大概! CustomRadarChatView继承自View,view默认的坐标轴是图左上角的坐标轴1,但是涉及到我们的图形时肯定不会很方便,所以我在程序里面用到了坐标轴2作辅助,方便计算。
我们的view是有它默认的大小的,也就是说你不能把它的width和height搞的太小,而且它最终是一个方形的,你也不可能将它拉伸,所以我用一个validRadius表示它的有效半径。然而需要注意的是,有效半径内也不全是我们的雷达图,不要忘记外面还有提示文字,提示文字是自动画出来的,你只需要把你想显示的文字告诉我们的view就行(默认是“默认”两个字),所以我们需要在外圈留一层空间,这里面的空间大小取决于maxTextRadius,我们会尽量让最长的文字也显示出来,剩下的空间才是雷达图的显示区域。所以在使用的过程中,希望你能注意不要把文字设置太长(一般也没人这么搞),控件有一个属性tipTextSize,你也可以根据它来调节字的大小。
在绘制文字时,我们需要知道文字处于坐标系2的哪个象限,原因很简单,你需要保持文字在多边形外边,所以这很重要。在这里我将多边形顶点的位置设定为最上边的position为0,顺时针递增。然后象限用一个数字表示,表示方法如下

/**
     * 根据位置获取当前点所在象限
     *
     * 第一、二、三、四象限分别为1,2,3,4
     *
     * x轴正向为5,y轴负向为6,x轴负向为7,y轴正向为8
     *
     *
     * @param position 位置
     * @return
     */
    private int getQuadrant(int position){
        int angle= (360*position/polygonsNum);
        if(angle==0)return 8;
        else if(angle==90)return 5;
        else if(angle==180)return 6;
        else if(angle==270)return 7;
        else if(angle>0&&angle<90)return 1;
        else if(angle>90&&angle<180)return 2;
        else if(angle>180&&angle<270)return 3;
        else return 4;
    }

绘制文字的代码就不贴出来了,很简单,有兴趣的同学可以下载我的源码查看。

重点来了,先看一下多边形是如何绘制出来的,因为多边形的边数和层数都是可以自定义的,所以我们需要用到两层循环,一层一层地画,每次画一圈。根据所在层数计算出最高点,根据角度确定位置,代码如下(建议有兴趣的同学自己推算一下,用到一点很简单的数学知识)

/**
     * 绘制多边形
     * @param canvas
     */

    private void drawPolygons(Canvas canvas) {
        for(int i=layerNum;i>=1;i--){

            Path path=new Path();
            float angle=360.0f/polygonsNum;
            float radius=(validRadius-maxTextRadius)/layerNum*i;
            float top=centerY-radius;
            path.moveTo(centerX,top);
            for(int j=1;j<polygonsNum;j++){
                path.lineTo(
                (float) (centerX+radius*Math.sin(Math.toRadians(angle*j))),
                (float) (radius-radius*Math.cos(Math.toRadians(angle*j))+top));
            }
            path.close();
            canvas.drawPath(path,polygonsPaints.get(i-1));
        }
    }

然后是放射线的绘制,首先说一下放射线是怎样形成的,你应该想的到。我们将多边形的每个顶点与中心连线,可以得到很多条边,这些边上分别取一个点,连接起来就是我们的放射线了。我们要做的就是根据用户传进来当前顶点对应的level值(最大值为0,最小值为1)来确定该点在边上的位置,没错,这个问题放在坐标系2就会变简单很多,先看代码

/**
     * 绘制放射线
     * @param canvas
     */
    private void drawLevel(Canvas canvas) {
        float radius=(validRadius-maxTextRadius);
        float top=centerY-radius;
        
        Path path=new Path();
        path.moveTo(centerX,getTranslateY(top,0));
        float angle=360.0f/polygonsNum;

        for(int i=1;i<polygonsNum;i++){
            path.lineTo( getTranslateX(centerX+radius*Math.sin(Math.toRadians(angle*i)),i)
                    ,getTranslateY(radius-radius*Math.cos(Math.toRadians(angle*i))+top,i));
        }

        path.close();
        canvas.drawPath(path,levelPaint);
        
    }

上面用到的了getTranslateX和getTranslateY,这两个函数将当前边的最外顶点坐标和当前边的position值传进去之后,会返回该边上点的坐标。
是怎么做到的呢?我拿getTranslateX举个例子,y是一样的道理

 private float getTranslateX(double x,int position){
        return (float) ((x-centerX)*getLevel(position)+centerX);
    }

没错,知道了该边的position,我们就知道了它的level值,level是个百分比(从0到1),然后转换到坐标轴2按照百分比求出该边上点的值,然后再将该点的值转化为坐标轴1的坐标值,化简之后就是那个式子。(你可能听的一头雾水,不过没关系,你可以自己推一遍)

使用注意事项

该控件默认层数为四层、默认边数为7
你可以在xml中使用如下属性

        <attr name="polygonsNum" format="integer"/>//边数
        <attr name="layerNum" format="integer"/>//层数
        <attr name="splitLineColor" format="color"/>//分割线颜色
        <attr name="levelLineColor" format="color"/>//放射线颜色
        <attr name="tipTextColor" format="color"/>//文字颜色
        <attr name="tipTextSize" format="dimension"/>//文字大小

也可以在代码中使用下列方法


    /**
     * 设置提示字画笔
     * @param textPaint
     */
    public void setTipTextPaint(Paint textPaint){}

    /**
     * 设置提示字颜色
     * @param color
     */
    public void setTipTextColor(int color){}

    /**
     * 设置提示字大小
     * @param size
     */
    public void setTipTextSize(int size){ }

    /**
     * 设置分割线颜色
     * @param color
     */
    public void setSplitLineColor(int color){}

    /**
     * 设置分割线画笔
     * @param paint
     */
    public void setSplitLinePaint(Paint paint){}


    /**
     * 设置放射线画笔
     * @param paint
     */
    public void setLevelPaint(Paint paint){}

    /**
     * 设置分割线颜色
     * @param color
     */
    public void setLevelLineColor(int color){}


    /**
     * 设置提示字
     * @param texts
     */
    public void setTipText(List<String>texts){ }


    /**
     * 设置每一层的颜色
     * @param colors
     */
    public void setLayer(List<Integer>colors){ }

    /**
     * 获取多边形的边数
     * @return
     */
    public int getPolygonsNum() { }

    /**
     * 设置多边形的边数
     * @param polygonsNum
     */
    public void setPolygonsNum(int polygonsNum) { }

    /**
     * 获取层数
     * @return
     */
    public int getLayerNum() { }

    /**
     * 设置层数
     * @param layerNum
     */
    public void setLayerNum(int layerNum) { }
    /**
     * 获取当前位置对应的值 0-1
     * @param position
     * @return
     */
    public float getLevel(int position){}

    /**
     * 设置对应位置的值
     * @param position
     * @param value
     */
    public void setValue(int position,float value){ }

如果你设置了多层颜色,一定要记得将足够数目的color或者paint传进去,否则可能会带来错误。更多使用你可以参考我的demo,如果有问题欢迎和我联系,谢谢!

GitHub地址:自定义雷达图(模仿掌盟能力值分析效果 )
微博:蝎子莱莱的微博
[1]: http://www.jianshu.com/p/d916791f0bfd

    原文作者:蝎子莱莱123
    原文地址: https://www.jianshu.com/p/0588b182eb64
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞