android自定义View(四)、柱形图、饼状图、折线图绘制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wanggang514260663/article/details/85125026

对着这部分主要是对canvas绘制线条、path的使用,内容比较简单,没有什么可详细说的价值,所以直接看代码。

1、折线图

public class LineChatView extends View {

    private int width, height;
    private Paint guidePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint dotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private int padding = 24;
    private int textRectWidth = 60;

    private int[] vData = {0, 20, 40, 60, 80, 100, 120, 140};
    private int[] hData = {2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017};
    private LineChatData[] paintDatas = {
            new LineChatData(2009, 20),
            new LineChatData(2010, 30),
            new LineChatData(2011, 85),
            new LineChatData(2012, 40),
            new LineChatData(2013, 60),
            new LineChatData(2014, 75),
            new LineChatData(2015, 135),
            new LineChatData(2016, 140),
            new LineChatData(2017, 90)
    };

    public LineChatView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        linePaint.setStrokeWidth(3);
        linePaint.setColor(Color.WHITE);
        linePaint.setStyle(Paint.Style.STROKE);

        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.WHITE);
        paint.setStrokeWidth(3);
        paint.setTextSize(24);

        guidePaint.setStrokeWidth(1.5F);
        guidePaint.setColor(Color.parseColor("#80FFFFFF"));
        guidePaint.setStyle(Paint.Style.STROKE);

        dotPaint.setStyle(Paint.Style.FILL);
        dotPaint.setStrokeWidth(12);
        dotPaint.setColor(Color.YELLOW);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.e("info", w + "," + h);
        width = w;
        height = h;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        setMeasuredDimension(widthMeasureSpec, 
        MeasureSpec.makeMeasureSpec(widthSize * 3 / 4, MeasureSpec.EXACTLY));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //绘制数学坐标系
        canvas.drawColor(Color.parseColor("#F14400"));

        //绘制坐标系
        canvas.drawLine(padding + textRectWidth, height - textRectWidth - padding,
                width - padding, height - textRectWidth - padding, linePaint);
        canvas.drawLine(padding + textRectWidth, height - textRectWidth - padding,
                padding + textRectWidth, padding, linePaint);

        //移动坐标系为当前的绘制坐标系
        canvas.translate(padding + textRectWidth, height - textRectWidth - padding);

        //绘制文字
        int paintHeight = (height - textRectWidth - 2 * padding) / vData.length;
        for (int i = 0; i < vData.length; i++) {
            canvas.drawText(String.valueOf(vData[i]), -textRectWidth, -paintHeight * i, paint);
        }

        int paintWidth = (width - 2 * padding - textRectWidth) / hData.length;
        for (int i = 0; i < hData.length; i++) {
            canvas.drawText(String.valueOf(hData[i]), paintWidth * i, textRectWidth / 3 * 2, paint);
        }

        //绘制分栏辅助线
        for (int i = 1; i <= vData.length; i++) {
            canvas.drawLine(0, -paintHeight * i, 
            width - 2 * padding - textRectWidth, -paintHeight * i, guidePaint);
        }

        //绘制折线
        Path linePath = new Path();
        int paintMaxHeight = height - textRectWidth - 2 * padding;
        for (int i = 0; i < paintDatas.length; i++) {
            if (i == 0) {
                linePath.moveTo(paintWidth * i, -paintDatas[i].value * (paintMaxHeight / 140));
            }
            linePath.lineTo(paintWidth * i, -paintDatas[i].value * (paintMaxHeight / 140));
        }
        canvas.drawPath(linePath, linePaint);

        //绘制数据折线点
        for (int i = 0; i < paintDatas.length; i++) {
            canvas.drawPoint(paintWidth * i, -paintDatas[i].value * (paintMaxHeight / 140), dotPaint);
        }
    }

    public static class LineChatData {
        private int year;
        private int value;

        public LineChatData(int year, int value) {
            this.year = year;
            this.value = value;
        }

        public int getYear() {
            return year;
        }

        public int getValue() {
            return value;
        }
    }
}

2、饼状图

public class PieChatView extends View {

    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint piePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private int width;
    private int height;
    private int radius;
    private static final int padding = 20;

    private ComparingRule[] comparingRules;

    public PieChatView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        comparingRules = new ComparingRule[]{
                new ComparingRule(Color.parseColor("#1d5464"), "射手", 90),
                new ComparingRule(Color.parseColor("#c1224f"), "坦克", 75),
                new ComparingRule(Color.parseColor("#0b032d"), "辅助", 162),
                new ComparingRule(Color.parseColor("#6b76ff"), "打野", 48),
                new ComparingRule(Color.parseColor("#45b7b7"), "法师", 250),
        };
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        width = w;
        height = h;

        radius = h / 4 * 3 / 2;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //绘制比例尺
        canvas.save();
        canvas.translate(0, height);  //移动坐标系 方便绘制
        float ruleWidth = (width - 2 * padding) / 4 * 3;
        float startWidth = width - padding - ruleWidth;
        float ruleItemWidth = ruleWidth / comparingRules.length;
        for (int i = 0; i < comparingRules.length; i++) {
            paint.setColor(Color.parseColor("#071e3d"));
            paint.setStrokeWidth(2);
            paint.setTextSize(24);
            paint.setStyle(Paint.Style.STROKE);
            Rect fontRect = new Rect();
            paint.getTextBounds(comparingRules[i].label, 0, comparingRules[i].label.length(), fontRect);
            canvas.drawText(comparingRules[i].label, 
            startWidth + i * ruleItemWidth + fontRect.width() / 2, -fontRect.height() / 2 - padding, paint);
            paint.setColor(comparingRules[i].color);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawRect(new RectF(startWidth + i * ruleItemWidth - 10, -fontRect.height() - padding - 10,
                    startWidth + i * ruleItemWidth + 10, -fontRect.height() - padding + 10), paint);
            paint.reset();
        }
        canvas.restore();

        //绘制饼状图
        float countSize = 0;
        for (int i = 0; i < comparingRules.length; i++) {
            countSize += comparingRules[i].radius;
        }
        //每一度的所占数据
        canvas.save();
        canvas.translate(getWidth() / 2, getHeight() / 2);
        float singleSize = countSize / 360F;
        float startRadius = 0;
        for (int i = 0; i < comparingRules.length; i++) {
            piePaint.setColor(comparingRules[i].color);
            piePaint.setStyle(Paint.Style.FILL);
            canvas.drawArc(new RectF(-radius, -radius, radius, radius), 
            startRadius, comparingRules[i].radius / singleSize, true, piePaint);

            piePaint.setStyle(Paint.Style.STROKE);
            piePaint.setColor(Color.BLACK);
            piePaint.setStrokeWidth(3);

            //绘制占比比例,先计算每个块的弧度中心点坐标,然后划线绘制文字
            float pStartX = (float) (Math.cos(
            Math.toRadians(startRadius + comparingRules[i].radius / singleSize / 2)) * radius);
            float pStartY = (float) (Math.sin(
            Math.toRadians(startRadius + comparingRules[i].radius / singleSize / 2)) * radius);

            float pEndX = (float) (Math.cos(
            Math.toRadians(startRadius + comparingRules[i].radius / singleSize / 2)) * (radius + 40));
            float pEndY = (float) (Math.sin(
            Math.toRadians(startRadius + comparingRules[i].radius / singleSize / 2)) * (radius + 40));
            canvas.drawLine(pStartX, pStartY, pEndX, pEndY, piePaint);

            paint.setStrokeWidth(2);
            paint.setStyle(Paint.Style.STROKE);
            paint.setTextSize(24);
            paint.setColor(Color.BLACK);

            BigDecimal decimal = new BigDecimal(comparingRules[i].radius / countSize * 100);
            String text = decimal.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue() +"%";
            Rect textRect = new Rect();
            paint.getTextBounds(text, 0, text.length(), textRect);

            if (pEndX < 0) {
                canvas.drawLine(pEndX, pEndY, pEndX - 60, pEndY, piePaint);
                canvas.drawText(text, pEndX - 60 - textRect.width(), pEndY, paint);
            } else {
                canvas.drawLine(pEndX, pEndY, pEndX + 60, pEndY, piePaint);
                canvas.drawText(text, pEndX + 60, pEndY, paint);
            }

            startRadius += comparingRules[i].radius / singleSize;
        }
        canvas.restore();
    }

    private static class ComparingRule {

        private int color;
        private String label;
        private int radius;

        private ComparingRule(int color, String label, int radius) {
            this.color = color;
            this.label = label;
            this.radius = radius;
        }

        public int getColor() {
            return color;
        }

        public String getLabel() {
            return label;
        }

        public int getRadius() {
            return radius;
        }
    }
}

3、柱形图

这里有一个技术点,就是柱形图上分别绘制了两种颜色,这个要使用到SweepGradient类。

 /**
     * A Shader that draws a sweep gradient around a center point.
     *
     * @param cx       The x-coordinate of the center
     * @param cy       The y-coordinate of the center
     * @param colors   The colors to be distributed between around the center.
     *                 There must be at least 2 colors in the array.
     * @param positions May be NULL. The relative position of
     *                 each corresponding color in the colors array, beginning
     *                 with 0 and ending with 1.0. If the values are not
     *                 monotonic, the drawing may produce unexpected results.
     *                 If positions is NULL, then the colors are automatically
     *                 spaced evenly.
     */
    public SweepGradient(float cx, float cy,
            @NonNull @ColorInt int colors[], @Nullable float positions[])

使用梯度绘制,就可以确保一次将该内容全部绘制出来,节省性能开销。

public class HistogramView extends View {

    private int width, height;
    private Paint guidePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private int padding = 24;
    private int textRectWidth = 60;

    private int[] vData = {0, 20, 40, 60, 80, 100, 120, 140};
    private int[] hData = {2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017};
    private LineChatData[] paintDatas = {
            new LineChatData(2009, 20, 80),
            new LineChatData(2010, 40, 10),
            new LineChatData(2011, 45, 75),
            new LineChatData(2012, 40, 15),
            new LineChatData(2013, 60, 28),
            new LineChatData(2014, 75, 25),
            new LineChatData(2015, 105, 25),
            new LineChatData(2016, 30, 20),
            new LineChatData(2017, 90, 5)
    };

    private SweepGradient gradient;

    public HistogramView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        linePaint.setStrokeWidth(3);
        linePaint.setColor(Color.BLACK);
        linePaint.setStyle(Paint.Style.STROKE);

        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.BLACK);
        paint.setStrokeWidth(3);
        paint.setTextSize(24);

        guidePaint.setStrokeWidth(1.5F);
        guidePaint.setColor(Color.parseColor("#80000000"));
        guidePaint.setStyle(Paint.Style.STROKE);

        rectPaint.setStyle(Paint.Style.FILL);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        setMeasuredDimension(widthMeasureSpec, 
        MeasureSpec.makeMeasureSpec(widthSize * 3 / 4, MeasureSpec.EXACTLY));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawColor(Color.parseColor("#ebebe3"));

        //绘制坐标系
        canvas.drawLine(padding + textRectWidth, 
        height - textRectWidth - padding, width - padding, height - textRectWidth - padding, linePaint);
        canvas.drawLine(padding + textRectWidth, 
        height - textRectWidth - padding, padding + textRectWidth, padding, linePaint);

        //移动坐标系为当前的绘制坐标系
        canvas.translate(padding + textRectWidth, height - textRectWidth - padding);

        //绘制文字
        int paintHeight = (height - textRectWidth - 2 * padding) / vData.length;
        for (int i = 0; i < vData.length; i++) {
            canvas.drawText(String.valueOf(vData[i]), -textRectWidth, -paintHeight * i, paint);
        }

        int paintWidth = (width - 2 * padding - textRectWidth) / hData.length;
        for (int i = 0; i < hData.length; i++) {
            canvas.drawText(String.valueOf(hData[i]), paintWidth * i, textRectWidth / 3 * 2, paint);
        }

        //绘制分栏辅助线
        for (int i = 1; i <= vData.length; i++) {
            canvas.drawLine(0, -paintHeight * i, 
            width - 2 * padding - textRectWidth, -paintHeight * i, guidePaint);
        }

        //绘制颜色
        int paintMaxHeight = height - textRectWidth - 2 * padding;

        for (int i = 0; i < paintDatas.length; i++) {
            Path path = new Path();
            gradient = new SweepGradient(paintWidth * i, 
            -(paintDatas[i].value) * (paintMaxHeight / 140), new int[]{
                    Color.parseColor("#207e82"),
                    Color.parseColor("#f05d23"),
            }, null);
            rectPaint.setShader(gradient);
            path.addRect(new RectF(paintWidth * i, 
            -(paintDatas[i].value + paintDatas[i].value2) * 
            (paintMaxHeight / 140), paintWidth * i + 40, 0), Path.Direction.CW);
            canvas.drawPath(path, rectPaint);
        }
    }

    public static class LineChatData {
        private int year;
        private int value;
        private int value2;

        public LineChatData(int year, int value, int value2) {
            this.year = year;
            this.value = value;
            this.value2 = value2;
        }

        public int getYear() {
            return year;
        }

        public int getValue() {
            return value;
        }

        public int getValue2() {
            return value2;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/wanggang514260663/article/details/85125026