自定义View练习

来源:互联网 发布:淘宝的宝贝网址怎么看 编辑:程序博客网 时间:2024/05/19 19:41

自定义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地址:

原创粉丝点击