天天看点

自定义View -- 蜘蛛网图

经常会在游戏中看到用蜘蛛网图表达用户在游戏中的各种表现,感觉还蛮直观蛮有趣的,刚好最近也在学习自定义View的相关知识,就自己做一个蜘蛛网图,作为笔记了。

先放一张我感觉对自己挺有帮助,从网上找的以这个自定义View流程的图片

自定义View -- 蜘蛛网图

View最终效果

自定义View -- 蜘蛛网图

  我们可以根据我们最终的效果来分析我们应该如何怎么完成这个View。

  首先可以看到最终的View是以整个布局的中心作为坐标原点开始绘制的,所以先要确定布局中心,然后最基础的是有几个共同中心点的正六边形,再将每个六边形的顶点与中心点相连,根据数据绘制需要覆盖的区域,最后在每个顶点旁标注文字。

  整体的流程就是这样了,可以开始写代码了

定义变量和初始化

private int count = ;           //数据个数
    private float angle = (float) (Math.PI *  / count);
    private float radius;            //网格最大半径
    private int centerX;             //中心X
    private int centerY;             //中心Y
    private String[] titles = {"a", "b", "c", "d", "e", "f"};                          //默认数据
    private double[] data = {, , , , , , , };                       //默认分值
    private float maxValue = ;    //数据最大值
    private Paint mainPaint;         //雷达区画笔
    private Paint valuePaint;        //数据区画笔
    private Paint textPaint;         //文本画笔
           
private void init() {
        count = Math.min(data.length, titles.length);

        mainPaint = new Paint();
        mainPaint.setAntiAlias(true);
        mainPaint.setColor(Color.GRAY);
        mainPaint.setStyle(Paint.Style.STROKE);

        valuePaint = new Paint();
        valuePaint.setAntiAlias(true);
        valuePaint.setColor(Color.BLUE);
        valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);

        textPaint = new Paint();
        textPaint.setTextSize();
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setColor(Color.BLACK);
    }
           

确定布局中心

onSizeChanged(int w, int h, int oldw, int oldh)方法里面,根据View的长宽,获取整个布局的中心坐标

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        radius = Math.min(h, w) /  * f;
        centerX = w / ;
        centerY = h / ;
        postInvalidate();
        super.onSizeChanged(w, h, oldw, oldh);
    }
           

绘制正六边形

private void drawPolygon(Canvas canvas) {
        Path path = new Path();
        float r = radius / (count - );
        for (int i = ; i < count; i++) {
            float curR = r * i;
            path.reset();
            for (int j = ; j < count; j++) {
                if (j == ) {
                    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);
        }
    }
           
自定义View -- 蜘蛛网图

绘制顶点到中心直线

private void drawLines(Canvas canvas) {
        Path path = new Path();
        for (int i = ; 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);
        }
    }
           
自定义View -- 蜘蛛网图

绘制覆盖区域

private void drawRegion(Canvas canvas) {
        Path path = new Path();
        valuePaint.setAlpha();
        for (int i = ; 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 == ) {
                path.moveTo(x, centerY);
            } else {
                path.lineTo(x, y);
            }
            //绘制小圆点
            canvas.drawCircle(x, y, , valuePaint);
        }
        valuePaint.setStyle(Paint.Style.STROKE);
        canvas.drawPath(path, valuePaint);
        valuePaint.setAlpha();
        //绘制填充区域
        valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        canvas.drawPath(path, valuePaint);
    }
           
自定义View -- 蜘蛛网图

绘制文本

对于文本的绘制,首先要找到末端的坐标,由于末端和文本有一定距离,给每个末端加上这个距离以后,再绘制文本。 另外,当文本在左边时,由于不希望文本和蜘蛛网交叉,我们可以先计算出文本的长度,然后使起始绘制坐标向左偏移这个长度。

private void drawText(Canvas canvas) {
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float fontHeight = fontMetrics.descent - fontMetrics.ascent;
        for (int i = ; i < count; i++) {
            float x = (float) (centerX + (radius + fontHeight / ) * Math.cos(angle * i));
            float y = (float) (centerY + (radius + fontHeight / ) * Math.sin(angle * i));
            if (angle * i >=  && angle * i <= Math.PI / ) {
                canvas.drawText(titles[i], x, y, textPaint);
            } else if (angle * i >=  * Math.PI /  && angle * i <= Math.PI * ) {
                canvas.drawText(titles[i], x, y, textPaint);
            } else if (angle * i > Math.PI /  && angle * i <= Math.PI) {
                float dis = textPaint.measureText(titles[i]);
                canvas.drawText(titles[i], x - dis, y, textPaint);
            } else if (angle * i >= Math.PI && angle * i <  * Math.PI / ) {
                float dis = textPaint.measureText(titles[i]);
                canvas.drawText(titles[i], x - dis, y, textPaint);
            }
        }
    }
           
自定义View -- 蜘蛛网图

到这里就完成了蜘蛛网图的自定义View绘制。