纵有疾风起
人生不言弃

Android雷达图(蜘蛛网图),自定义view之雷达图,正五边雷达图,分数图

最近业务要做分数雷达图,到网上找了很大,原理都差不多,但是要适用自己的业务,还需要微调。下面是我微调后的效果图

Android雷达图(蜘蛛网图),自定义view之雷达图,正五边雷达图,分数图插图
雷达图蜘蛛网.png

原理很简单

  • 1,确定雷达图中心点的坐标
  • 2,用正三角sin,反三角函数cos计算五个类别的坐标点
  • 3,绘制蜘蛛网
  • 4,绘制直线
  • 5,绘制覆盖区域(包含红点)

下面结合代码给大家讲解下

-1, 下面是需要用到的一些属性

public class RadarView extends View {    //数据个数    private int count = 5;    //成绩圆点半径    private int valueRadius = 8;    //网格最大半径    private float radius;    //中心X    private float centerX;    //中心Y    private float centerY;    //雷达区画笔    private Paint mainPaint;    //文本画笔    private Paint textPaint;    //数据区画笔    private Paint valuePaint;    //标题文字    private List<String> titles;    //各维度分值    private List<Double> data;    //数据最大值    private double maxValue = 100;    //弧度    private float angle;}
  • 2,获取中心点坐标
    这个是在view的onSizeChanged方法里获取当前view的宽高,取宽高的小值,然后再找中心点作为雷达图的中心点。这样做的好处是可以使雷达图自适应
 @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        radius = Math.min(w, h) / 2 * 0.8f;        centerX = w / 2;        centerY = h / 2;        //一旦size发生改变,重新绘制        postInvalidate();        super.onSizeChanged(w, h, oldw, oldh);    }
  • 3,开始绘制
 @Override    protected void onDraw(Canvas canvas) {        drawPolygon(canvas);//绘制蜘蛛网        drawLines(canvas);//绘制直线        drawTitle(canvas);//绘制标题        drawRegion(canvas);//绘制覆盖区域    }

下面贴出所有代码,代码里有很详细的注释

package com.example.qcl.demo.radarmap;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.View;import java.util.ArrayList;import java.util.List;/** * 2018/6/15 11:09 * Created by qcl * wechat:2501902696 */public class RadarView extends View {    //数据个数    private int count = 5;    //成绩圆点半径    private int valueRadius = 8;    //网格最大半径    private float radius;    //中心X    private float centerX;    //中心Y    private float centerY;    //雷达区画笔    private Paint mainPaint;    //文本画笔    private Paint textPaint;    //数据区画笔    private Paint valuePaint;    //标题文字    private List<String> titles;    //各维度分值    private List<Double> data;    //数据最大值    private double maxValue = 100;    //弧度    private float angle;    public RadarView(Context context) {        this(context, null);    }    public RadarView(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public RadarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        //雷达区画笔初始化        mainPaint = new Paint();        mainPaint.setColor(Color.BLACK);        mainPaint.setAntiAlias(true);        mainPaint.setStrokeWidth(1);        mainPaint.setStyle(Paint.Style.STROKE);        //文本画笔初始化        textPaint = new Paint();        textPaint.setColor(Color.BLACK);        textPaint.setTextAlign(Paint.Align.CENTER);        textPaint.setTextSize(30);        textPaint.setStrokeWidth(1);        textPaint.setAntiAlias(true);        //数据区(分数)画笔初始化        valuePaint = new Paint();        valuePaint.setColor(Color.RED);        valuePaint.setAntiAlias(true);        valuePaint.setStyle(Paint.Style.FILL);        titles = new ArrayList<>();        titles.add("语文");        titles.add("数学");        titles.add("英语");        titles.add("政治");        titles.add("历史");        count = titles.size();        //默认分数        data = new ArrayList<>(count);        data.add(100.0);        data.add(80.0);        data.add(90.0);        data.add(70.0);        data.add(60.0);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        radius = Math.min(w, h) / 2 * 0.8f;        centerX = w / 2;        centerY = h / 2;        //一旦size发生改变,重新绘制        postInvalidate();        super.onSizeChanged(w, h, oldw, oldh);    }    @Override    protected void onDraw(Canvas canvas) {        drawPolygon(canvas);//绘制蜘蛛网        drawLines(canvas);//绘制直线        drawTitle(canvas);//绘制标题        drawRegion(canvas);//绘制覆盖区域    }    /**     * 绘制多边形     *     * @param canvas     */    private void drawPolygon(Canvas canvas) {        Path path = new Path();        //1度=1*PI/180   360度=2*PI   那么我们每旋转一次的角度为2*PI/内角个数        //中心与相邻两个内角相连的夹角角度        angle = (float) (2 * Math.PI / count);        //每个蛛丝之间的间距        float r = radius / (count - 1);        for (int i = 0; i < count; i++) {            //当前半径            float curR = r * i;            path.reset();            for (int j = 0; j < count; j++) {                if (j == 0) {                    float x = (float) (centerX + curR * Math.sin(angle));                    float y = (float) (centerY - curR * Math.cos(angle));                    path.moveTo(x, y);                } else {                    //根据半径,计算出蜘蛛丝上每个点的坐标                    float x1 = (float) (centerX + curR * Math.sin(angle / 2));                    float y1 = (float) (centerY + curR * Math.cos(angle / 2));                    path.lineTo(x1, y1);                    float x2 = (float) (centerX - curR * Math.sin(angle / 2));                    float y2 = (float) (centerY + curR * Math.cos(angle / 2));                    path.lineTo(x2, y2);                    float x3 = (float) (centerX - curR * Math.sin(angle));                    float y3 = (float) (centerY - curR * Math.cos(angle));                    path.lineTo(x3, y3);                    float x4 = centerX;                    float y4 = centerY - curR;                    path.lineTo(x4, y4);                    float x = (float) (centerX + curR * Math.sin(angle));                    float y = (float) (centerY - curR * Math.cos(angle));                    path.lineTo(x, y);                }            }            path.close();            canvas.drawPath(path, mainPaint);        }    }    /**     * 绘制直线     */    private void drawLines(Canvas canvas) {        Path path = new Path();        path.reset();        //直线1        path.moveTo(centerX, centerY);        float x1 = (float) (centerX + radius * Math.sin(angle));        float y1 = (float) (centerY - radius * Math.cos(angle));        path.lineTo(x1, y1);        //直线2        path.moveTo(centerX, centerY);        float x2 = (float) (centerX + radius * Math.sin(angle / 2));        float y2 = (float) (centerY + radius * Math.cos(angle / 2));        path.lineTo(x2, y2);        //直线3        path.moveTo(centerX, centerY);        float x3 = (float) (centerX - radius * Math.sin(angle / 2));        float y3 = (float) (centerY + radius * Math.cos(angle / 2));        path.lineTo(x3, y3);        //直线4        path.moveTo(centerX, centerY);        float x4 = (float) (centerX - radius * Math.sin(angle));        float y4 = (float) (centerY - radius * Math.cos(angle));        path.lineTo(x4, y4);        //直线5        path.moveTo(centerX, centerY);        float x5 = (float) (centerX);        float y5 = (float) (centerY - radius);        path.lineTo(x5, y5);        path.close();        canvas.drawPath(path, mainPaint);    }    /**     * 绘制标题文字     *     * @param canvas     */    private void drawTitle(Canvas canvas) {        if (count != titles.size()) {            return;        }        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();        float fontHeight = fontMetrics.descent - fontMetrics.ascent;//标题高度        //绘制文字1        float x1 = centerX;        float y1 = centerY - radius;        canvas.drawText(titles.get(0), x1, y1 - fontHeight / 5, textPaint);        //绘制文字2        float x2 = (float) (centerX + radius * Math.sin(angle));        float y2 = (float) (centerY - radius * Math.cos(angle));        float dis = textPaint.measureText(titles.get(1));//标题一半的宽度        canvas.drawText(titles.get(1), x2 + dis, y2 + fontHeight / 5, textPaint);        //绘制文字3        float x3 = (float) (centerX + radius * Math.sin(angle / 2));        float y3 = (float) (centerY + radius * Math.cos(angle / 2));        canvas.drawText(titles.get(2), x3, y3 + fontHeight, textPaint);        //绘制文字4        float x4 = (float) (centerX - radius * Math.sin(angle / 2));        float y4 = (float) (centerY + radius * Math.cos(angle / 2));        canvas.drawText(titles.get(3), x4, y4 + fontHeight, textPaint);        //绘制文字5        float x5 = (float) (centerX - radius * Math.sin(angle));        float y5 = (float) (centerY - radius * Math.cos(angle));        float dis5 = textPaint.measureText(titles.get(1));//标题的宽度        canvas.drawText(titles.get(4), x5 - dis5, y5 - fontHeight / 5, textPaint);    }    /**     * 绘制覆盖区域     */    private void drawRegion(Canvas canvas) {        valuePaint.setAlpha(255);        Path path = new Path();        double dataValue;        double percent;        //绘制圆点1        dataValue = data.get(0);        if (dataValue != maxValue) {            percent = dataValue / maxValue;        } else {            percent = 1;        }        float x1 = centerX;        float y1 = (float) (centerY - radius * percent);        path.moveTo(x1, y1);        canvas.drawCircle(x1, y1, valueRadius, valuePaint);        //绘制圆点2        dataValue = data.get(1);        if (dataValue != maxValue) {            percent = dataValue / maxValue;        } else {            percent = 1;        }        float x2 = (float) (centerX + radius * percent * Math.sin(angle));        float y2 = (float) (centerY - radius * percent * Math.cos(angle));        path.lineTo(x2, y2);        canvas.drawCircle(x2, y2, valueRadius, valuePaint);        //绘制圆点3        dataValue = data.get(2);        if (dataValue != maxValue) {            percent = dataValue / maxValue;        } else {            percent = 1;        }        float x3 = (float) (centerX + radius * percent * Math.sin(angle / 2));        float y3 = (float) (centerY + radius * percent * Math.cos(angle / 2));        path.lineTo(x3, y3);        canvas.drawCircle(x3, y3, valueRadius, valuePaint);        //绘制圆点4        dataValue = data.get(3);        if (dataValue != maxValue) {            percent = dataValue / maxValue;        } else {            percent = 1;        }        float x4 = (float) (centerX - radius * percent * Math.sin(angle / 2));        float y4 = (float) (centerY + radius * percent * Math.cos(angle / 2));        path.lineTo(x4, y4);        canvas.drawCircle(x4, y4, valueRadius, valuePaint);        //绘制圆点5        dataValue = data.get(3);        if (dataValue != maxValue) {            percent = dataValue / maxValue;        } else {            percent = 1;        }        float x5 = (float) (centerX - radius * percent * Math.sin(angle));        float y5 = (float) (centerY - radius * percent * Math.cos(angle));        path.lineTo(x5, y5);        canvas.drawCircle(x5, y5, valueRadius, valuePaint);        path.close();        valuePaint.setStyle(Paint.Style.STROKE);        //绘制覆盖区域外的连线        canvas.drawPath(path, valuePaint);        //填充覆盖区域        valuePaint.setAlpha(128);        valuePaint.setStyle(Paint.Style.FILL);        canvas.drawPath(path, valuePaint);    }    //设置蜘蛛网颜色    public void setMainPaint(Paint mainPaint) {        this.mainPaint = mainPaint;        postInvalidate();    }    //设置标题颜色    public void setTextPaint(Paint textPaint) {        this.textPaint = textPaint;    }    //设置覆盖局域颜色    public void setValuePaint(Paint valuePaint) {        this.valuePaint = valuePaint;        postInvalidate();    }    //设置各门得分    public void setData(List<Double> data) {        this.data = data;        postInvalidate();    }    //设置满分分数,默认是100分满分    public void setMaxValue(float maxValue) {        this.maxValue = maxValue;    }}

使用

  • 1,在布局文件中
<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@color/grey666"    android:orientation="vertical">    <com.example.qcl.demo.radarmap.RadarView        android:id="@+id/radarview"        android:layout_width="250dp"        android:layout_height="200dp"        android:background="@color/colorWhite"/>    <com.example.qcl.demo.radarmap.RadarView        android:layout_width="250dp"        android:layout_height="200dp"        android:layout_marginTop="30dp"        android:background="@color/colorWhite"/></LinearLayout>

在activity中

public class RadarMapActivity extends AppCompatActivity {   @Override   protected void onCreate(Bundle savedInstanceState) {       super.onCreate(savedInstanceState);       setContentView(R.layout.activity_radar_map);       RadarView radarview = findViewById(R.id.radarview);       List<Double> datas = new ArrayList<>();       datas.add(100.0);//语文100分       datas.add(80.0);//数学80分       datas.add(90.0);//英语90分       datas.add(70.0);//政治70分       datas.add(60.0);//历史60分       radarview.setData(datas);   }}

显示样式

Android雷达图(蜘蛛网图),自定义view之雷达图,正五边雷达图,分数图插图1
蜘蛛网.png

源码地址:https://github.com/qiushi123/demo3

文章转载于:https://www.jianshu.com/p/6359adfc032a

原著是一个有趣的人,若有侵权,请通知删除

未经允许不得转载:起风网 » Android雷达图(蜘蛛网图),自定义view之雷达图,正五边雷达图,分数图
分享到: 生成海报

评论 抢沙发

评论前必须登录!

立即登录