美文网首页android自定义viewAndroid自定义View安卓自定义VIEW
Android雷达图(蜘蛛网图),自定义view之雷达图,正五边

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

作者: 编程小石头666 | 来源:发表于2018-06-15 12:33 被阅读5次

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


    雷达图蜘蛛网.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);
       }
    }
    

    显示样式


    蜘蛛网.png

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

    相关文章

      网友评论

        本文标题:Android雷达图(蜘蛛网图),自定义view之雷达图,正五边

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