自定义View练习 - 雷达信用图表

本文介绍了一个自定义View的练习,通过绘制雷达图来展示芝麻信用分数。内容包括练习的来源、最终效果展示、绘制思路及详细实现过程,如计算点坐标、绘制多边形、连接线、填充区域、总分数和标题图标。同时,文章强调了获取文字宽高、计算坐标等关键知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自定义View练习 - 雷达信用图表

练习来源

Android自定义控件 芝麻信用分雷达图

练习的github:https://github.com/alidili/SesameCreditScore

最终效果图

雷达信用图表

思路

  1. 绘制多边形
  2. 绘制连接线
  3. 绘制填充区域
  4. 绘制总分数
  5. 绘制标题和图标

实现过程

0. 定义变量

    //数据的数量
    private int dataCount;
    //弧度
    private float radian;
    //多边形的外接圆的半径
    private int radius;
    //中心点x
    private int centerX;
    //中心点y
    private int centerY;

    private List<CreditBean> creditBeans;
    //最大分值
    public int maxScore;

    private Paint linePaint;
    private Paint regionPaint;
    private Paint totalScorePaint;
    private Paint titlePaint;
1. 计算点坐标
  • 计算坐标的示意图
  • 计算坐标示意图
    /**
     * 获得点坐标
     */
    public Point getPoint(int index) {
        return getPoint(index, 1);
    }

    /**
     * 获得点坐标,percent为百分比,用于获得填充区域和文字等的坐标位置.
     * 坐标的计算是重难点. 可以画图分析.
     */
    public Point getPoint(int index, float percent) {
        int x = 0;
        int y = 0;
        if (index == 0) {
            x = (int) (centerX + radius * Math.sin(radian) * percent);
            y = (int) (centerY - radius * Math.cos(radian) * percent);
        } else if (index == 1) {
            x = (int) (centerX + radius * Math.sin(radian * 1.0f / 2) * percent);
            y = (int) (centerY + radius * Math.cos(radian * 1.0f / 2) * percent);
        } else if (index == 2) {
            x = (int) (centerX - radius * Math.sin(radian * 1.0f / 2) * percent);
            y = (int) (centerY + radius * Math.cos(radian * 1.0f / 2) * percent);
        } else if (index == 3) {
            x = (int) (centerX - radius * Math.sin(radian) * percent);
            y = (int) (centerY - radius * Math.cos(radian) * percent);
        } else if (index == 4) {
            x = centerX;
            y = (int) (centerY - radius * percent);
        }
        return new Point(x, y);
    }
2. 绘制过程
    @Override
    protected void onDraw(Canvas canvas) {
        //绘制多边形
        drawPolygon(canvas);
        //绘制连接线
        drawLines(canvas);
        //绘制填充区域
        drawRegion(canvas);
        //绘制总分数
        drawTotalScore(canvas);
        //绘制标题和图标
        drawTitleAndIcons(canvas);
    }
  • 绘制多边形
    • 这里注意需要闭合路径
    /**
     * 绘制多边形
     */
    private void drawPolygon(Canvas canvas) {
        Path polygonPath = new Path();
        for (int i = 0; i < dataCount; i++) {
            if (i == 0) {
                polygonPath.moveTo(getPoint(i).x, getPoint(i).y);
            } else {
                polygonPath.lineTo(getPoint(i).x, getPoint(i).y);
            }
        }
        polygonPath.close();
        canvas.drawPath(polygonPath, linePaint);
    }
  • 绘制连接线
    • 直接连线从圆心到各个顶点

    /**
     * 绘制连接线
     */
    private void drawLines(Canvas canvas) {
        for (int i = 0; i < dataCount; i++) {
            canvas.drawLine(centerX, centerY, getPoint(i).x, getPoint(i).y, linePaint);
        }
    }
  • 绘制填充区域
    • 这里的获得百分比坐标的方式可以注意下.
    /**
     * 绘制填充区域
     */
    private void drawRegion(Canvas canvas) {
        Path regionPath = new Path();
        float percent;
        for (int i = 0; i < creditBeans.size(); i++) {
            percent = creditBeans.get(i).score * 1.0f / maxScore;
            int x = getPoint(i, percent).x;
            int y = getPoint(i, percent).y;
            if (i == 0) {
                regionPath.moveTo(x, y);
            } else {
                regionPath.lineTo(x, y);
            }
        }
        regionPath.close();
        canvas.drawPath(regionPath, regionPaint);
    }
  • 绘制总分数
    • 总分数的文字绘制在正中心.
    • 这里要注意文字的宽度和高度的获得方式.
    /**
     * 绘制总分数
     */
    private void drawTotalScore(Canvas canvas) {
        int totalScore = 0;
        for (CreditBean creditBean : creditBeans) {
            totalScore += creditBean.score;
        }
        //获得文字宽度
        float textWidth = totalScorePaint.measureText(String.valueOf(totalScore));
        Paint.FontMetrics fontMetrics = totalScorePaint.getFontMetrics();
        //获得文字高度
        float textHeight = Math.abs(fontMetrics.ascent) - fontMetrics.descent;
        canvas.drawText(String.valueOf(totalScore), centerX - textWidth / 2, centerY + textHeight / 2, totalScorePaint);
    }
  • 绘制标题和图标
    • 图标和文字位置是根据文字和图标的宽高计算的.
    /**
     * 绘制标题和图标
     */
    private void drawTitleAndIcons(Canvas canvas) {
        for (int i = 0; i < creditBeans.size(); i++) {
            String title = creditBeans.get(i).title;
            int textX = getPoint(i, 1.2f).x;
            int textY = getPoint(i, 1.2f).y;

            //获得文字的宽高
            int textWidth = (int) titlePaint.measureText(title);
            Paint.FontMetrics fontMetrics = titlePaint.getFontMetrics();
            int textHeight = (int) (Math.abs(fontMetrics.ascent) - fontMetrics.descent);

            //获得图标的宽高
            Bitmap icon = BitmapFactory.decodeResource(getResources(), creditBeans.get(i).icon);
            int iconWidth = icon.getWidth();
            int iconHeight = icon.getHeight();
            if (i == 0) {
                //不需要修正位置
            } else if (i == 1) {
                textY += textHeight;
            } else if (i == 2) {
                textX -= textWidth;
                textY += textHeight;
            } else if (i == 3) {
                textX -= textWidth;
            } else {
                textX -= textWidth / 2;
            }
            int iconX = textX + textWidth / 2 - iconWidth / 2;
            int iconY = textY - textHeight - iconHeight - DensityUtils.dp2px(getContext(), 8);
            canvas.drawText(title, textX, textY, titlePaint);
            canvas.drawBitmap(icon, iconX, iconY, null);
        }
    }
3. 设置数据
    /**
     * 设置数据
     */
    public void setData(List<CreditBean> creditBeans, int maxScore) {
        this.creditBeans = creditBeans;
        this.dataCount = creditBeans.size();
        this.maxScore = maxScore;
        this.radian = (float) (Math.PI * 2 * 1.0f / dataCount);
    }

学到的知识

  1. 文字的宽度和高度的获得
        //获得文字宽度
        float textWidth = totalScorePaint.measureText(String.valueOf(totalScore));
        //获得文字高度
        Paint.FontMetrics fontMetrics = totalScorePaint.getFontMetrics();
        float textHeight = Math.abs(fontMetrics.ascent) - fontMetrics.descent;
  1. 获得多边形每个顶点的位置, 坐标的计算是个重点.
    注意: Math.sin() 参数是弧度. 如果需要将角度转为弧度, 可以使用Math.toRadian().
  2. 计算填充区域顶点坐标和多边形的顶点是在同一条过圆心的直线上. 这里使用了percent来获得两个坐标.
    /**
     * 获得点坐标
     */
    public Point getPoint(int index) {
        return getPoint(index, 1);
    }

    /**
     * 获得点坐标,percent为百分比,用于获得填充区域和文字等的坐标位置.
     */
    public Point getPoint(int index, float percent) {
        int x = 0;
        int y = 0;
        if (index == 0) {
            x = (int) (centerX + radius * Math.sin(radian) * percent);
            y = (int) (centerY - radius * Math.cos(radian) * percent);
        } else if (index == 1) {
            x = (int) (centerX + radius * Math.sin(radian * 1.0f / 2) * percent);
            y = (int) (centerY + radius * Math.cos(radian * 1.0f / 2) * percent);
        } else if (index == 2) {
            x = (int) (centerX - radius * Math.sin(radian * 1.0f / 2) * percent);
            y = (int) (centerY + radius * Math.cos(radian * 1.0f / 2) * percent);
        } else if (index == 3) {
            x = (int) (centerX - radius * Math.sin(radian) * percent);
            y = (int) (centerY - radius * Math.cos(radian) * percent);
        } else if (index == 4) {
            x = centerX;
            y = (int) (centerY - radius * percent);
        }
        return new Point(x, y);
    }

github地址:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值