欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android自定义雷达图

程序员文章站 2022-03-11 19:13:28
...
/**
 * 雷达view
 * 站不住
 * 2018-06-21
 */

public class RadarView extends View{
    private int centerX;                  //中心X
    private int centerY;                  //中心Y
    private float radius;                   //网格最大半径
    private Paint mainPaint;            //雷达区画笔
    private Paint textPaint;                //文本画笔
    private Paint valuePaint;               //数据区画笔
    private int count = 6;                //数据个数
    private float angle = (float) (Math.PI*2/count);

    private String[] titles = {"a","b","c","d","e","f"};

    private double[] data = {100,60,60,60,100,50,10,20}; //各维度分值
    private float maxValue = 100;             //数据最大值

    public RadarView(Context context) {
        this(context,null);
    }

    public RadarView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        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.RED);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(32);
        textPaint.setStyle(Paint.Style.STROKE);

        valuePaint=new Paint();
//        覆盖区域,只要使用path记录下坐标点,然后设
        valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        valuePaint.setColor(Color.BLUE);
        valuePaint.setAntiAlias(true);
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        centerX=w/2;
        centerY=h/2;
        radius = Math.min(h, w)/2*0.8f;
        Log.d("===onSizeChanged======",Thread.currentThread().getName());
        //一旦size发生改变,重新绘制(onDraw)
//        postInvalidate(); //工作在子线程
        invalidate(); // 工作在ui线程
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawPolygon(canvas);
        drawLines(canvas);
        drawText(canvas);
        drawRegion(canvas);
    }
    /**
     * 绘制正多边形
     */
    private void drawPolygon(Canvas canvas) {
        Path path = new Path();
        float r = radius/(count-1);//r是蜘蛛丝之间的间距

        for(int i=1;i<count;i++){//中心点不用绘制

            float curR = r*i;//当前半径
            path.reset();

            for(int j=0;j<count;j++){
                if(j==0){
                    //每一圈设置起点
                    path.moveTo(centerX+curR,centerY);
                }else{
                    //根据半径,计算出蜘蛛丝上每个点的坐标
                    float x = (float) (centerX+curR*Math.cos(angle*j));
                    float y = (float) (centerY+curR*Math.sin(angle*j));
                    path.lineTo(x,y);
                }
            }

            path.close();//闭合路径
            canvas.drawPath(path, mainPaint);
        }
    }

    /**
     * 绘制直线
     * 绘制从中心到末端的直线
     * 同样根据半径,计算出每个末端坐标
     */
    private void drawLines(Canvas canvas){
        Path path = new Path();
        for(int i=0;i<count;i++){
            path.reset();
            //设置每一次的起点为中心
            path.moveTo(centerX, centerY);
            float x = (float) (centerX+radius*Math.cos(angle*i));
            float y = (float) (centerY+radius*Math.sin(angle*i));
            //画线
            path.lineTo(x, y);
            canvas.drawPath(path, mainPaint);
        }
    }

    /**
     * 绘制文字
     * @param canvas
     * 对于文本的绘制,首先要找到末端的坐标,
     * 由于末端和文本有一定距离,给每个末端加上这个距离以后,再绘制文本。
        另外,当文本在左边时,由于不希望文本和蜘蛛网交叉,
        我们可以先计算出文本的长度,然后使起始绘制坐标向左偏移这个长度。
     */
    private void drawText(Canvas canvas){
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float fontHeight = fontMetrics.descent - fontMetrics.ascent;
        for(int i=0;i<count;i++){
            float x = (float) (centerX+(radius+fontHeight/2)*Math.cos(angle*i));
            float y = (float) (centerY+(radius+fontHeight/2)*Math.sin(angle*i));
            if(angle*i>=0&&angle*i<=Math.PI/2){//第4象限
                canvas.drawText(titles[i], x,y,textPaint);
            }else if(angle*i>=3*Math.PI/2&&angle*i<=Math.PI*2){//第3象限
                canvas.drawText(titles[i], x,y,textPaint);
            }else if(angle*i>Math.PI/2&&angle*i<=Math.PI){//第2象限
                float dis = textPaint.measureText(titles[i]);//文本长度
                canvas.drawText(titles[i], x-dis,y,textPaint);
            }else if(angle*i>=Math.PI&&angle*i<3*Math.PI/2){//第1象限
                float dis = textPaint.measureText(titles[i]);//文本长度
                canvas.drawText(titles[i], x-dis,y,textPaint);
            }
        }
    }

    /**
     * 绘制区域
     * @param canvas
     *使path包围区域被填充
     *
     */
    private void drawRegion(Canvas canvas){
        Path path = new Path();
        valuePaint.setAlpha(255);
        for(int i=0;i<count;i++){
            double percent = data[i]/maxValue;
            float x = (float) (centerX+radius*Math.cos(angle*i)*percent);
            float y = (float) (centerY+radius*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自定义雷达图

最终的实现效果就是这样的