手动撸个Android雷达图(蜘蛛网图)RadarView

公司产品需要一个雷达图来展示各维度的比重,网上找了一波,学到不少,直接自己上手来撸一记

无图言虚空

简单分析一波,确定雷达图正几边形的--正五边形 int count=5,分为几个层数--4 层 int layerCount=4

    @Override

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawPolygon(canvas);//画边
        drawLines(canvas);//画线
        drawText(canvas);//描绘文字
        drawRegion(canvas);//覆盖区域
    }

主要这几步,开撸!

自定义RadarView继承View

确定需要使用的变量,初始化paint,计算圆心角

private int count = 5; //几边形   
private int layerCount = 4; //层数   
privatefloatangle; //每条边对应的圆心角   
private int centerX; //圆心x   
private int centerY; //圆心y   
privatefloatradius; //半径   
private Paint polygonPaint; //边框paint   
private Paint linePaint; //连线paint   
private Paint txtPaint; //文字paint   
private Paint circlePaint; //圆点paint   
private Paint regionColorPaint; //覆盖区域paint   
private Double[] percents = {0.91, 0.35, 0.12, 0.8, 0.5}; //覆盖区域百分比   
private String[] titles = {"dota","斗地主","大吉大利,晚上吃鸡","炉石传说","跳一跳"};//文字
    public RadarView(Context context) {
        this(context, null, 0);
    }

    public RadarView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RadarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //计算圆心角
        angle = (float) (Math.PI * 2 / count);

        polygonPaint = new Paint();
        polygonPaint.setColor(ContextCompat.getColor(context, R.color.radarPolygonColor));
        polygonPaint.setAntiAlias(true);
        polygonPaint.setStyle(Paint.Style.STROKE);
        polygonPaint.setStrokeWidth(4f);

        linePaint = new Paint();
        linePaint.setColor(ContextCompat.getColor(context, R.color.radarLineColor));
        linePaint.setAntiAlias(true);
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setStrokeWidth(2f);

        txtPaint = new Paint();
        txtPaint.setColor(ContextCompat.getColor(context, R.color.radarTxtColor));
        txtPaint.setAntiAlias(true);
        txtPaint.setStyle(Paint.Style.STROKE);
        txtPaint.setTextSize(DensityUtil.dpToPx(context, 12));

        circlePaint = new Paint();
        circlePaint.setColor(ContextCompat.getColor(context, R.color.radarCircleColor));
        circlePaint.setAntiAlias(true);

        regionColorPaint = new Paint();
        regionColorPaint.setColor(ContextCompat.getColor(context, R.color.radarRegionColor));
        regionColorPaint.setStyle(Paint.Style.FILL);
        regionColorPaint.setAntiAlias(true);

    }

确定中心点

需要正五边形得有一个圆,圆内接正五边形,在onSizeChanged方法里获取圆心,确定半径

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

radius = Math.min(h, w) / 2 * 0.7f;

centerX = w / 2;

centerY = h / 2;

}

绘制正五边形

绘制正五边形同时描绘最外围的点,确定分为4层,半径 / 层数 =每层之间的间距,从最里层开始画正五边形,每层第一个点位于中心点正上方

  private void drawPolygon(Canvas canvas) {
        Path path = new Path();
        float r = radius / layerCount;
        for (int i = 1; i <= layerCount; i++) {
            float curR = r * i; //当前所在层的半径
            for (int j = 0; j < count; j++) {
                if (j == 0) {
                    //每一层第一个点坐标
                    path.moveTo(centerX, centerY - curR);
                } else {
                    //顺时针记录其余顶角的点坐标
                    float x = (float) (centerX + Math.sin(angle * j) * curR);
                    float y = (float) (centerY - Math.cos(angle * j) * curR);
                    path.lineTo(x, y);
                }
            }
            //最外层的顶角外面的五个小圆点(图中红色部分)
            if (i == layerCount) {
                for (int j = 0; j < count; j++) {
                    float x = (float) (centerX + Math.sin(angle * j) * (curR + 12));
                    float y = (float) (centerY - Math.cos(angle * j) * (curR + 12));
                    canvas.drawCircle(x, y, 4, circlePaint);
                }
            }
            path.close();
            canvas.drawPath(path, polygonPaint);
        }
    }

绘制连线

绘制最内层顶角到最外层顶角的连线

  private void drawLines(Canvas canvas) {
        float r = radius / layerCount;
        for (int i = 0; i < count; i++) {
            //起始坐标 从中心开始的话 startx=centerX , startY=centerY
            float startX = (float) (centerX + Math.sin(angle * i) * r);
            float startY = (float) (centerY - Math.cos(angle * i) * r);
            //末端坐标
            float endX = (float) (centerX + Math.sin(angle * i) * radius);
            float endY = (float) (centerY - Math.cos(angle * i) * radius);
            canvas.drawLine(startX, startY, endX, endY, linePaint);
        }
    }

至此简易雷达图成型,可以修改正几边形,多少层数(后续继续添加文字)

    //设置几边形,**注意:设置几边形需要重新计算圆心角**
    public void setCount(int count){
        this.count = count;
        angle = (float) (Math.PI * 2 / count);
        invalidate();
    }

    //设置层数
    public void setLayerCount(int layerCount){
        this.layerCount = layerCount;
        invalidate();
    }

设置正六边形、六层

radarView.setCount(6);

radarView.setLayerCount(6);

对于以下图形的,可以设置第一个点坐标位于中心点正右侧(centerX+curR,centerY),顺时针计算其余顶点坐标x = (float) (centerX+curR*Math.cos(angle*j)), y = (float) (centerY+curR*Math.sin(angle*j)),同理连线等其余坐标相应改变...

描绘文字

由于各产品维度内容不同,所需雷达图样式不一,这里只是描绘下不同位置的文字处理情况,具体需求还得按产品来,因产品而异

    private void drawText(Canvas canvas) {
        for (int i = 0; i < count; i++) {
            //获取到雷达图最外边的坐标
            float x = (float) (centerX + Math.sin(angle * i) * (radius + 12));
            float y = (float) (centerY - Math.cos(angle * i) * (radius + 12));
            if (angle * i == 0) {
                //第一个文字位于顶角正上方
                txtPaint.setTextAlign(Paint.Align.CENTER);
                canvas.drawText(titles[i], x, y - 18, txtPaint);
                txtPaint.setTextAlign(Paint.Align.LEFT);
            } else if (angle * i > 0 && angle * i < Math.PI / 2) {
                //微调
                canvas.drawText(titles[i], x + 18, y + 10, txtPaint);
            } else if (angle * i >= Math.PI / 2 && angle * i < Math.PI) {
                //最右下的文字获取到文字的长、宽,按文字长度百分比向左移
                String txt = titles[i];
                Rect bounds = new Rect();
                txtPaint.getTextBounds(txt, 0, txt.length(), bounds);
                float height = bounds.bottom - bounds.top;
                float width = txtPaint.measureText(txt);
                canvas.drawText(txt, x - width * 0.4f, y + height + 18, txtPaint);
            } else if (angle * i >= Math.PI && angle * i < 3 * Math.PI / 2) {
                //同理最左下的文字获取到文字的长、宽,按文字长度百分比向左移
                String txt = titles[i];
                Rect bounds = new Rect();
                txtPaint.getTextBounds(txt, 0, txt.length(), bounds);
                float width = txtPaint.measureText(txt);
                float height = bounds.bottom - bounds.top;
                canvas.drawText(txt, x - width * 0.6f, y + height + 18, txtPaint);
            } else if (angle * i >= 3 * Math.PI / 2 && angle * i < 2 * Math.PI) {
                //文字向左移动
                String txt = titles[i];
                float width = txtPaint.measureText(txt);
                canvas.drawText(txt, x - width - 18, y + 10, txtPaint);
            }

        }
    }

绘制覆盖区域

绘制覆盖区域,百分比取连线长度的百分比(如果从中心点开始的连线,则是半径的百分比),此处用半径radius减去间隔r即连线长度

   private void drawRegion(Canvas canvas) {
        Path path = new Path();
        float r = radius / layerCount;//每层的间距
        for (int i = 0; i < count; i++) {
            if (i == 0) {
                path.moveTo(centerX, (float) (centerY - r - (radius - r) * percents[i]));
            } else {
                float x = (float) (centerX + Math.sin(angle * i) * (percents[i] * (radius - r) + r));
                float y = (float) (centerY - Math.cos(angle * i) * (percents[i] * (radius - r) + r));
                path.lineTo(x, y);
            }
        }
        path.close();
        canvas.drawPath(path, regionColorPaint);
    }

至此,一个简单的雷达图完毕。同时欢迎关注微信公众号

End

原文地址:https://www.cnblogs.com/taixiang/p/9032955.html

时间: 2024-10-09 13:24:13

手动撸个Android雷达图(蜘蛛网图)RadarView的相关文章

Android雷达图(蜘蛛网图)

简介 最近因为项目的需要,需要实现雷达图来展示各科目的对题率. 雷达图的绘制不算复杂,只要按照一定的流程来绘制就可以了,其中使用的最多的是path路径类,使用这个类便于我们绘制出多边形等效果. 效果图如下: 使用方式 使用方式很简单,直接在布局文件里面使用这个控件,记得设置一个合适的大小就可以. 当然也有开放一些public方法,可以进行数据.文本颜色等设置. /** * 设置数据 * @param points */ public void setData(ArrayList<LastPoin

自定义蜘蛛网图 NetView

概述 写论文忙里偷闲写了一个蜘蛛网图的自定义view,支持多重属性 有图才能有真相,下面先上图 主要支持网格颜色.tag文本.覆盖区域颜色.透明度的属性改变,具体使用可以参见我的githubgithub完整下载,求互粉求start,下面主要介绍一下实现这个自定义控件的几个关键点. 确定属性 首先要知道这个控件需要暴露出哪些属性,创建一个attrs.xml <?xml version="1.0" encoding="utf-8"?> <resourc

javascript雷达图/星形图/极坐标图Radar &amp; Polar chart介绍

Radar & Polar chart 控件为软件开发人员提供了雷达图/星形图/极坐标图,它以轴上的同一个点为起点,绘制出三个或更多的变量. 具体功能: 任意多的轴 动态或静态 简单.堆积.100%堆积的图表 网格线能转换为圆环 在轴之间进行填充 以自定义范围值填充 跨平台 导出为图像 可通过JavaScript进行控制 可直接通过HTML文件进行设置和数据交换 自定义图像和Flash(动态)背景 无限的标签 可旋转的标签 可使用预设频率重新加载数据 格式任何气球文本 Amcharts可以从简单

Android MediaPlayer 框架UML图

Android开发交流群:50342056 目的 本文用一个UML类图,讲解mp3文件播放的框架流程.内容以下几个方面: 1.UML类图 2.stagefrightPlayer是如何创建的: 3.mp3文件的解析和解码的简单介绍 4.播放mp3文件过程中,生产者和消费者的关系: 5.openmax和stagefright框架的消息机制 UML类图 stagefrightPlayer是如何创建的 对照着UML图,看下StagefrightPlayer创建的过程. 故事的开始是Java层的Media

Android 特效View第四弹之折线图 心率图

Android 特效View第四弹之折线图 心率图 <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.empty.ChartView android:id="@+id/chart" android:layout_width="wrap_content" and

关于Android Studio中点9图的编译错误问题

Android中的点9图想必大家都非常熟悉了,能够指定背景图片的缩放区域和文本内容的显示区域,常见如QQ聊天界面的背景气泡这种文本内容不固定并需要适配的应用场景. 这里也给大家准备了一张图,详细介绍了一下"Android设计中的.9.png",大家可以点击放大查看: 以前在使用Eclipse时候很少会出现点9图的编译错误问题,但是在转向Android Studio的过程中,你可能会遇到.比如你将Eclipse项目转化为Android Studio项目,或者在自己的项目中引入点9图,然后

Android版CAD看图软件如何打开CAD图纸

CAD图纸的查看现在不仅仅只局限于使用电脑查看,它也可以使用手机查看,今天教大家的就是如何使用Android版CAD看图查看图纸! 第一步:首先要去到手机应用商店中下载一款CAD看图软件,小编使用的就是迅捷CAD看图,需要的可以去到应用商店中下载 第二步:下载完成后在桌面中找到迅捷CAD看图软件并进入 第三步:如果你是第一次打开上面最近打开的显示中有一个示例文件可供参考 第四步:今天就以示例为例点击打开就好了 如果您还想了解更多或是想要下载其他有关CAD方面的软件的话,你可以直接进入我们的官网h

UML建模之 - 类图&amp;时序图

简介 简介 UML是面向对象开发中一种通用的图形化建模语言 UML建模技术就是用模型元素来组建整个系统的模型,模型元素包括系统中的类.类和类之间的关联.类的实例相互配合实现系统的动态行为等 UML建模工具 Rational Rose : 是直接从UML发展而诞生的设计工具,常用建模工具 Power Designer : 是对数据库建模而发展起来的一种数据库建模工具 Microsoft Visio :是一种画图工具,Visio 2010才开始引入UML建模,不是很专业 Star UML : 开源轻

php验证码+缩略图+饼状图+五环图

@1.验证码 1 captcher.php 2 header('Content-type:image/png'); 3 session_start(); 4 $img = imagecreate(100, 30); 5 $captcha = array( 6 'a', 'b', 'c', 'd', 'e', 'f', 7 'g', 'h', 'i', 'j', 'k', 'l', 8 'm', 'n', 'o', 'p', 'q', 'r', 9 's', 't', 'u', 'v', 'w',