Android 自定义雷达图 水平柱状图 垂直柱状图 扇形图

一.自定义雷达图

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.graphics.Path;

import java.util.ArrayList;
import java.util.List;
/**
 * Created by ${qc} on 2018/12/18.
 * 雷达图
 */

public class CustomRadarView extends View {
    //数据个数
    private int count = 5;
    //成绩圆点半径
    private int valueRadius = 8;
    //网格最大半径
    private float radius;
    //中心X
    private float centerX;
    //中心Y
    private float centerY;
    //雷达区画笔
    private Paint mainPaint;
    //文本画笔
    private Paint textPaint;
    //数据区画笔
    private Paint valuePaint;
    //标题文字
    private List titles;
    //各维度分值
    private List data;
    //数据最大值
    private double maxValue = 100;
    //弧度
    private float angle;


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

    public CustomRadarView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomRadarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        //雷达区画笔初始化
        mainPaint = new Paint();
        mainPaint.setColor(Color.parseColor("#4DB6AC"));
        mainPaint.setAntiAlias(true);
        mainPaint.setStrokeWidth(1);
        mainPaint.setStyle(Paint.Style.STROKE);
        //文本画笔初始化
        textPaint = new Paint();
        textPaint.setColor(Color.BLACK);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setTextSize(30);
        textPaint.setStrokeWidth(1);
        textPaint.setAntiAlias(true);
        //数据区(分数)画笔初始化
        valuePaint = new Paint();
        valuePaint.setColor(Color.parseColor("#4DB6AC"));
        valuePaint.setAntiAlias(true);
        valuePaint.setStyle(Paint.Style.FILL);

        //默认的title
        titles = new ArrayList<>();
        titles.add("语法");
        titles.add("听力");
        titles.add("口语");
        titles.add("写作");
        titles.add("阅读理解");
        count = titles.size();

        //默认分数
        data = new ArrayList<>(count);
        data.add(100.0);
        data.add(80.0);
        data.add(90.0);
        data.add(70.0);
        data.add(60.0);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        radius = Math.min(w, h) / 2 * 0.8f;
        centerX = w / 2;
        centerY = h / 2;
        //一旦size发生改变,重新绘制
        postInvalidate();
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        drawPolygon(canvas);//绘制蜘蛛网
        drawLines(canvas);//绘制直线
        drawTitle(canvas);//绘制标题
        drawRegion(canvas);//绘制覆盖区域
    }

    /**
     * 绘制多边形
     *
     * @param canvas
     */
    private void drawPolygon(Canvas canvas) {
        Path path = new Path();
        //1度=1*PI/180   360度=2*PI   那么我们每旋转一次的角度为2*PI/内角个数
        //中心与相邻两个内角相连的夹角角度
        angle = (float) (2 * Math.PI / count);
        //每个蛛丝之间的间距
        float r = radius / (count - 1);

        for (int i = 0; i < count; i++) {
            //当前半径
            float curR = r * i;
            path.reset();
            for (int j = 0; j < count; j++) {
                if (j == 0) {
                    float x = (float) (centerX + curR * Math.sin(angle));
                    float y = (float) (centerY - curR * Math.cos(angle));
                    path.moveTo(x, y);
                } else {
                    //根据半径,计算出蜘蛛丝上每个点的坐标
                    float x1 = (float) (centerX + curR * Math.sin(angle / 2));
                    float y1 = (float) (centerY + curR * Math.cos(angle / 2));
                    path.lineTo(x1, y1);
                    float x2 = (float) (centerX - curR * Math.sin(angle / 2));
                    float y2 = (float) (centerY + curR * Math.cos(angle / 2));
                    path.lineTo(x2, y2);
                    float x3 = (float) (centerX - curR * Math.sin(angle));
                    float y3 = (float) (centerY - curR * Math.cos(angle));
                    path.lineTo(x3, y3);
                    float x4 = centerX;
                    float y4 = centerY - curR;
                    path.lineTo(x4, y4);
                    float x = (float) (centerX + curR * Math.sin(angle));
                    float y = (float) (centerY - curR * Math.cos(angle));
                    path.lineTo(x, y);
                }
            }
            path.close();
            canvas.drawPath(path, mainPaint);
        }
    }


    /**
     * 绘制直线
     */
    private void drawLines(Canvas canvas) {
        Path path = new Path();
        path.reset();
        //直线1
        path.moveTo(centerX, centerY);
        float x1 = (float) (centerX + radius * Math.sin(angle));
        float y1 = (float) (centerY - radius * Math.cos(angle));
        path.lineTo(x1, y1);
        //直线2
        path.moveTo(centerX, centerY);
        float x2 = (float) (centerX + radius * Math.sin(angle / 2));
        float y2 = (float) (centerY + radius * Math.cos(angle / 2));
        path.lineTo(x2, y2);
        //直线3
        path.moveTo(centerX, centerY);
        float x3 = (float) (centerX - radius * Math.sin(angle / 2));
        float y3 = (float) (centerY + radius * Math.cos(angle / 2));
        path.lineTo(x3, y3);
        //直线4
        path.moveTo(centerX, centerY);
        float x4 = (float) (centerX - radius * Math.sin(angle));
        float y4 = (float) (centerY - radius * Math.cos(angle));
        path.lineTo(x4, y4);
        //直线5
        path.moveTo(centerX, centerY);
        float x5 = (float) (centerX);
        float y5 = (float) (centerY - radius);
        path.lineTo(x5, y5);
        path.close();
        canvas.drawPath(path, mainPaint);
    }

    /**
     * 绘制标题文字
     *
     * @param canvas
     */
    private void drawTitle(Canvas canvas) {
        if (count != titles.size()) {
            return;
        }
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float fontHeight = fontMetrics.descent - fontMetrics.ascent;//标题高度
        //绘制文字1
        float x1 = centerX;
        float y1 = centerY - radius;
        canvas.drawText(titles.get(0), x1, y1 - fontHeight / 5, textPaint);
        //绘制文字2
        float x2 = (float) (centerX + radius * Math.sin(angle));
        float y2 = (float) (centerY - radius * Math.cos(angle));
        float dis = textPaint.measureText(titles.get(1));//标题一半的宽度
        canvas.drawText(titles.get(1), x2 + dis, y2 + fontHeight / 5, textPaint);
        //绘制文字3
        float x3 = (float) (centerX + radius * Math.sin(angle / 2));
        float y3 = (float) (centerY + radius * Math.cos(angle / 2));
        canvas.drawText(titles.get(2), x3, y3 + fontHeight, textPaint);
        //绘制文字4
        float x4 = (float) (centerX - radius * Math.sin(angle / 2));
        float y4 = (float) (centerY + radius * Math.cos(angle / 2));
        canvas.drawText(titles.get(3), x4, y4 + fontHeight, textPaint);
        //绘制文字5
        float x5 = (float) (centerX - radius * Math.sin(angle));
        float y5 = (float) (centerY - radius * Math.cos(angle));
        float dis5 = textPaint.measureText(titles.get(1));//标题的宽度
        canvas.drawText(titles.get(4), x5 - dis5, y5 - fontHeight / 5, textPaint);
    }

    /**
     * 绘制覆盖区域
     */
    private void drawRegion(Canvas canvas) {
        valuePaint.setAlpha(255);
        Path path = new Path();
        double dataValue;
        double percent;
        //绘制圆点1
        dataValue = data.get(0);
        if (dataValue != maxValue) {
            percent = dataValue / maxValue;
        } else {
            percent = 1;
        }
        float x1 = centerX;
        float y1 = (float) (centerY - radius * percent);
        path.moveTo(x1, y1);
        canvas.drawCircle(x1, y1, valueRadius, valuePaint);
        //绘制圆点2
        dataValue = data.get(1);
        if (dataValue != maxValue) {
            percent = dataValue / maxValue;
        } else {
            percent = 1;
        }
        float x2 = (float) (centerX + radius * percent * Math.sin(angle));
        float y2 = (float) (centerY - radius * percent * Math.cos(angle));
        path.lineTo(x2, y2);
        canvas.drawCircle(x2, y2, valueRadius, valuePaint);
        //绘制圆点3
        dataValue = data.get(2);
        if (dataValue != maxValue) {
            percent = dataValue / maxValue;
        } else {
            percent = 1;
        }
        float x3 = (float) (centerX + radius * percent * Math.sin(angle / 2));
        float y3 = (float) (centerY + radius * percent * Math.cos(angle / 2));
        path.lineTo(x3, y3);
        canvas.drawCircle(x3, y3, valueRadius, valuePaint);
        //绘制圆点4
        dataValue = data.get(3);
        if (dataValue != maxValue) {
            percent = dataValue / maxValue;
        } else {
            percent = 1;
        }
        float x4 = (float) (centerX - radius * percent * Math.sin(angle / 2));
        float y4 = (float) (centerY + radius * percent * Math.cos(angle / 2));
        path.lineTo(x4, y4);
        canvas.drawCircle(x4, y4, valueRadius, valuePaint);
        //绘制圆点5
        dataValue = data.get(3);
        if (dataValue != maxValue) {
            percent = dataValue / maxValue;
        } else {
            percent = 1;
        }
        float x5 = (float) (centerX - radius * percent * Math.sin(angle));
        float y5 = (float) (centerY - radius * percent * Math.cos(angle));
        path.lineTo(x5, y5);
        canvas.drawCircle(x5, y5, valueRadius, valuePaint);

        path.close();
        valuePaint.setStyle(Paint.Style.STROKE);
        //绘制覆盖区域外的连线
        canvas.drawPath(path, valuePaint);
        //填充覆盖区域
        valuePaint.setAlpha(128);
        valuePaint.setStyle(Paint.Style.FILL);
        canvas.drawPath(path, valuePaint);
    }


    //设置蜘蛛网颜色
    public void setMainPaint(Paint mainPaint) {
        this.mainPaint = mainPaint;
        postInvalidate();
    }

    //设置标题颜色
    public void setTextPaint(Paint textPaint) {
        this.textPaint = textPaint;
    }

    //设置覆盖局域颜色
    public void setValuePaint(Paint valuePaint) {
        this.valuePaint = valuePaint;
        postInvalidate();
    }

    //设置各门得分
    public void setData(List data) {
        this.data = data;
        postInvalidate();
    }

    //设置各门title
    public void setValueTitle(List dataTitle){
        this.titles = dataTitle;
        postInvalidate();
    }
    //设置满分分数,默认是100分满分
    public void setMaxValue(float maxValue) {
        this.maxValue = maxValue;
    }
}

二.自定义水平柱状图

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.graphics.Path;
import java.util.List;

/**
 * Created by ${qc} on 2018/12/18.
 * 横向的条形统计图
 */

public class HorizontalChartView extends View{


    /**
     * 最大值
     */
    private float maxValue;
    /**
     * 统计项目
     */
    private List barList;

    /**
     * 线的宽度
     */
    private int lineStrokeWidth;
    /**
     * 统计条宽度
     */
    private int barWidth;

    /**
     * 两条统计图之间空间
     */
    private int barSpace;

    /**
     * 各画笔
     */
    private Paint barPaint, linePaint, textPaint, scoreTextPaint;

    /**
     * 矩形区域
     */
    private Rect barRect, topRect;

    private Path textPath;

    private int itemNameWidth;

    private int scoreTextHeight;

    /**
     * 项目名和条形图之间的距离
     */
    private int betweenMargin;


    public HorizontalChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    /**
     * 初始化设置
     */
    private void init(Context context) {
        barPaint = new Paint();
        barPaint.setColor(getResources().getColor(R.color.blue));

        linePaint = new Paint();
        linePaint.setColor(getResources().getColor(R.color.black));
        lineStrokeWidth = DensityUtil.dip2px(context, 0.5f);
        linePaint.setStrokeWidth(lineStrokeWidth);

        textPaint = new Paint();
        textPaint.setColor(getResources().getColor(R.color.black));
        textPaint.setTextSize(DensityUtil.dip2px(context, 13));
        textPaint.setAntiAlias(true);

        scoreTextPaint = new Paint();
        scoreTextPaint.setTextSize(DensityUtil.dip2px(context, 13));
        scoreTextPaint.setColor(getResources().getColor(R.color.white));

        barRect = new Rect(0, 0, 0, 0);
        textPath = new Path();

        barWidth = DensityUtil.dip2px(context, 18);
        barSpace = DensityUtil.dip2px(context, 20);
        scoreTextHeight = DensityUtil.dip2px(context, 13);
        itemNameWidth = DensityUtil.dip2px(context, 86);
        betweenMargin = scoreTextHeight / 2;

    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float lineViewWidth = (float) ((this.getWidth() - itemNameWidth) * 0.8);//线的宽度占总宽度的0.8,剩余的部分显示分数
        float scoreWidth = lineViewWidth / 5;
        int scoreAdd = (int) (maxValue / 5);
        if (isInEditMode()) {
            return;
        }
        for (int i = 0; i < barList.size(); i++) {
            barRect.left = itemNameWidth;
            barRect.top = barSpace * (i + 2) + barWidth * i;
            barRect.right = (int) (lineViewWidth * (barList.get(i).getScore() / maxValue)) + itemNameWidth;
            barRect.bottom = barRect.top + barWidth;
            if ((barList.get(i).getScore() / maxValue) >= 0.6) {
                barPaint.setColor(getResources().getColor(R.color.blue));
            } else {
                barPaint.setColor(getResources().getColor(R.color.red));
            }

            canvas.drawRect(barRect, barPaint);
            canvas.drawText(barList.get(i).getScore() + "分", barRect.right, barRect.bottom - (barWidth - scoreTextHeight), textPaint);
            canvas.drawText(barList.get(i).getItemName(), itemNameWidth - betweenMargin - textPaint.measureText(barList.get(i).getItemName()), barRect.bottom - (barWidth - scoreTextHeight), textPaint);
//            canvas.drawText(String.valueOf(scoreAdd * (i + 1)), itemNameWidth + scoreWidth * (i + 1) - textPaint.measureText(String.valueOf(scoreAdd * (i + 1))) / 2, barSpace, textPaint);
        }
//        canvas.drawLine(itemNameWidth, 0, itemNameWidth, this.getHeight(), linePaint);
    }


    /**
     * 设置统计项目列表
     *
     * @param barList
     */
    public void setBarList(List barList) {
        this.barList = barList;
        if (barList == null) {
            throw new RuntimeException("BarChartView.setItems(): the param items cannot be null.");
        }
        if (barList.size() == 0) {
            return;
        }
        maxValue = barList.get(0).getScore();
        for (BarBean bar : barList) {
            if (bar.getScore() > maxValue) {
                maxValue = bar.getScore();
            }
        }
        invalidate();

    }

    public static class BarBean{
        int score;
        String itemName;

        public BarBean(int score, String itemName) {
            this.score = score;
            this.itemName = itemName;
        }

        public int getScore() {
            return score;
        }

        public void setScore(int score) {
            this.score = score;
        }

        public String getItemName() {
            return itemName;
        }

        public void setItemName(String itemName) {
            this.itemName = itemName;
        }
    }

    //根据xml的设定获取宽度
    private int measureWidth(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        //wrap_content
        if (specMode == MeasureSpec.AT_MOST) {

        }
        //fill_parent或者精确值
        else if (specMode == MeasureSpec.EXACTLY) {

        }
        Log.i("这个控件的宽度----------", "specMode=" + specMode + " specSize=" + specSize);

        return specSize;
    }

    //根据xml的设定获取高度
    private int measureHeight(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        //wrap_content
        if (specMode == MeasureSpec.AT_MOST) {

        }
        //fill_parent或者精确值
        else if (specMode == MeasureSpec.EXACTLY) {

        }
        Log.i("这个控件的高度----------", "specMode:" + specMode + " specSize:" + specSize);

        return specSize;
    }
}

三.自定义垂直柱状图

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import android.graphics.Paint.FontMetricsInt;
import java.util.ArrayList;

/**
 * Created by ${qc} on 2018/12/18.
 * 条形图
 */

public class HistogramView extends View{

    private Paint mPaint;
    private Rect mRect;

    private int mWidth;
    private int mHeight;

    private int mPaddingStart;
    private int mPaddingEnd;
    private int mPaddingTop;
    private int mPaddingBottom;

    private int mLeft;
    private int mTop;
    private int mRight;
    private int mBottom;

    private Context mContext;

    private ArrayList mBarLists;

    public HistogramView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        initData();
    }

    public HistogramView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void initData() {

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mRect = new Rect();

        // default data
        mBarLists = new ArrayList();
        Bar bar1 = new Bar(1, 0.3f, Color.CYAN, "one", "30");
        Bar bar2 = new Bar(2, 0.55f, Color.GREEN, "two", "55");
        Bar bar3 = new Bar(3, 0.8f, Color.BLUE, "three", "80");
        mBarLists.add(bar1);
        mBarLists.add(bar2);
        mBarLists.add(bar3);
    }

    public void setBarLists(ArrayList barLists){
        mBarLists = barLists;
        postInvalidate();
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mWidth = getSizeFromMeasureSpec(widthMeasureSpec, 480);
        mHeight = getSizeFromMeasureSpec(heightMeasureSpec, 480);

        mPaddingStart = getPaddingStart();
        mPaddingEnd = getPaddingEnd();
        mPaddingTop = getPaddingTop();
        mPaddingBottom = getPaddingBottom();

        mLeft = mPaddingStart;
        mTop = mPaddingTop;
        mRight = mWidth - mPaddingEnd;
        mBottom = mHeight - mPaddingBottom;

        setMeasuredDimension(mWidth, mHeight);
    }


    @Override
    protected void onDraw(Canvas canvas) {

        // set background
        canvas.drawColor(Color.RED);
        mRect.set(mLeft, mTop, mRight, mBottom);
        mPaint.setColor(Color.WHITE);
        canvas.drawRect(mRect, mPaint);
        //*/

        // 设置底部文字属性
        mPaint.setTextSize(sp2Px(mContext, 11));
        mPaint.setTextAlign(Paint.Align.CENTER);
        FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt();
        int fontHeight = (int) Math.ceil(fontMetricsInt.bottom - fontMetricsInt.top);

        int N = mBarLists.size();
        int UNIT_WIDTH = (mRight - mLeft) / (2 * N + 1);

        int left = 0;
        int top = 0;
        int right = 0;
        int bottom = 0;

        // 逐个画bar
        for (int i = 0; i < N; i++) {
            Bar bar = mBarLists.get(i);

            // 画 bar 底部文字
            left = (int) (mLeft + (i * 2 + 0.5f) * UNIT_WIDTH);
            right = left + UNIT_WIDTH * 2;
            top = mBottom - fontHeight;
            bottom = mBottom;
            mRect.set(left, top, right, bottom);
            int baseLine = (mRect.top + mRect.bottom - fontMetricsInt.top - fontMetricsInt.bottom) / 2;
            mPaint.setColor(Color.BLACK);
            canvas.drawText(bar.bootomText, mRect.centerX(), baseLine, mPaint);

            // 画 bar 图形
            left = mLeft + (i * 2 + 1) * UNIT_WIDTH;
            right = left + UNIT_WIDTH;
            bottom = mBottom - fontHeight;
            top = bottom - (int) ((mBottom - mTop - fontHeight * 2) * bar.ratio);
            mRect.set(left, top, right, bottom);
            mPaint.setColor(bar.color);
            canvas.drawRect(mRect, mPaint);

            // 画 bar 顶部文字
            left = (int) (mLeft + (i * 2 + 0.5f) * UNIT_WIDTH);
            right = left + UNIT_WIDTH * 2;
            bottom = top;
            top = top - fontHeight;
            mRect.set(left, top, right, bottom);
            baseLine = (mRect.top + mRect.bottom - fontMetricsInt.top - fontMetricsInt.bottom) / 2;
            mPaint.setColor(Color.BLACK);
            canvas.drawText(bar.topText, mRect.centerX(), baseLine, mPaint);
        }

        // 画线
//        mPaint.setColor(Color.BLACK);
//        canvas.drawLine(mLeft, mBottom - fontHeight, mRight, mBottom - fontHeight, mPaint);
        // canvas.drawLine(mLeft, mTop + fontHeight, mRight, mTop + fontHeight, mPaint);

        super.onDraw(canvas);
    }

    public class Bar {
        public int id;
        public float ratio;
        public int color;
        public String bootomText;
        public String topText;


        public Bar(int id, float ratio, int color, String bootomText, String topText) {
            this.id = id;
            this.ratio = ratio;
            this.color = color;
            this.bootomText = bootomText;
            this.topText = topText;
        }
    }


    // 工具类
    public static int getSizeFromMeasureSpec(int measureSpec, int defaultSize) {
        int result = 0;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        if(mode == MeasureSpec.EXACTLY){
            result = size;
        } else {
            result = defaultSize;
            if(mode == MeasureSpec.AT_MOST){
                result = Math.min(defaultSize, size);
            }
        }
        return result;
    }
    public static float sp2Px(Context context, float sp){
        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager wm = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        display.getMetrics(metrics);
        float px = metrics.scaledDensity;
        return sp * px;
    }

}

四.自定义扇形图

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PathMeasure;
import android.graphics.Point;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;

import com.hjq.demo.R;

import java.util.ArrayList;
import java.util.List;
import android.graphics.Path;

/**
 * Created by ${qc} on 2018/12/18.
 */

public class RingView extends View {
    private Context mContext;
    private Paint mPaint;
    private int mPaintWidth = 0; // 画笔的宽
    private int topMargin = 30; // 上边距
    private int leftMargin = 80; // 左边距
    private Resources mRes;
    private DisplayMetrics dm;
    private int showRateSize = 10; // 展示文字的大小

    private int circleCenterX = 46; // 圆心点X  要与外圆半径相等
    private int circleCenterY = 46; // 圆心点Y  要与外圆半径相等

    private int ringOuterRidus = 46; // 外圆的半径
    private int ringInnerRidus = 13; // 内圆的半径
    private int ringPointRidus = 40; // 点所在圆的半径

    private float rate = 0.4f; //点的外延距离  与  点所在圆半径的长度比率
    private float extendLineWidth = 20; //点外延后  折的横线的长度

    private RectF rectF; // 外圆所在的矩形
    private RectF rectFPoint; // 点所在的矩形

    private List colorList;
    private List rateList;
    private List titleList;
    private boolean isRing;
    private boolean isShowCenterPoint;
    private boolean isShowRate;

    public RingView(Context context) {
        super(context, null);
    }

    public RingView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        initView();
    }

    public void setShow(List colorList, List rateList) {
        setShow(colorList, rateList, false);
    }

    public void setShow(List colorList, List rateList, boolean isRing) {
        setShow(colorList, rateList, isRing, false);
    }

    public void setShow(List colorList, List rateList, boolean isRing, boolean isShowRate) {
        setShow(colorList, rateList, isRing, isShowRate, false);
    }
    public void setData(List title){
        this.titleList=title;
    }

    public void setShow(List colorList, List rateList, boolean isRing, boolean isShowRate, boolean isShowCenterPoint) {
        this.colorList = colorList;
        this.rateList = rateList;
        this.isRing = isRing;
        this.isShowRate = isShowRate;
        this.isShowCenterPoint = isShowCenterPoint;
    }

    private void initView() {
        this.mRes = mContext.getResources();
        this.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        dm = new DisplayMetrics();
        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getMetrics(dm);
        int screenWidth = wm.getDefaultDisplay().getWidth();
//        int height = wm.getDefaultDisplay().getHeight();
        leftMargin = (px2dip(screenWidth) - (2 * circleCenterX)) / 2;
        mPaint.setColor(getResources().getColor(R.color.red));
        mPaint.setStrokeWidth(dip2px(mPaintWidth));
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setAntiAlias(true);
        rectF = new RectF(dip2px(mPaintWidth + leftMargin), dip2px(mPaintWidth + topMargin), dip2px(circleCenterX + ringOuterRidus + mPaintWidth * 2 + leftMargin), dip2px(circleCenterY + ringOuterRidus + mPaintWidth * 2 + topMargin));
        rectFPoint = new RectF(dip2px(mPaintWidth + leftMargin + (ringOuterRidus - ringPointRidus)), dip2px(mPaintWidth + topMargin + (ringOuterRidus - ringPointRidus)), dip2px(circleCenterX + ringPointRidus + mPaintWidth * 2 + leftMargin), dip2px(circleCenterY + ringPointRidus + mPaintWidth * 2 + topMargin));
        Log.e("矩形点:", dip2px(circleCenterX + ringOuterRidus + mPaintWidth * 2) + " --- " + dip2px(circleCenterY + ringOuterRidus + mPaintWidth * 2));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        pointList.clear();
        if (colorList != null) {
            for (int i = 0; i < colorList.size(); i++) {
                mPaint.setColor(mRes.getColor(colorList.get(i)));
                mPaint.setStyle(Paint.Style.FILL);
                drawOuter(canvas, i);
            }
        }
        mPaint.setStyle(Paint.Style.FILL);
        if (isRing) {
            drawInner(canvas);
        }
        if (isShowCenterPoint) {
            drawCenterPoint(canvas);
        }
    }

    private void drawCenterPoint(Canvas canvas) {
        mPaint.setColor(mRes.getColor(R.color.red)); //        Log.e("中心点:", dip2px(circleCenterX + mPaintWidth * 2 + leftMargin) + " --- " + dip2px(circleCenterY + mPaintWidth * 2 + topMargin));
        canvas.drawCircle(dip2px(circleCenterX + mPaintWidth * 2 + leftMargin), dip2px(circleCenterY + mPaintWidth * 2 + topMargin), dip2px(1), mPaint);
    }

    private void drawInner(Canvas canvas) {
        mPaint.setColor(mRes.getColor(R.color.white)); //        Log.e("内部圆点:", dip2px(circleCenterX + mPaintWidth * 2 + leftMargin) + " --- " + dip2px(circleCenterY + mPaintWidth * 2 + topMargin));
        canvas.drawCircle(dip2px(circleCenterX + mPaintWidth * 2 + leftMargin), dip2px(circleCenterY + mPaintWidth * 2 + topMargin), dip2px(ringInnerRidus), mPaint);
    }

    private float preRate;

    private void drawArcCenterPoint(Canvas canvas, int position) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(mRes.getColor(R.color.transparent));
        mPaint.setStrokeWidth(dip2px(1));
        canvas.drawArc(rectFPoint, preAngle, (endAngle) / 2, true, mPaint);
        dealPoint(rectFPoint, preAngle, (endAngle) / 2, pointArcCenterList);
        Point point = pointArcCenterList.get(position);
        mPaint.setColor(mRes.getColor(R.color.white));
        canvas.drawCircle(point.x, point.y, dip2px(2), mPaint);
        if (preRate / 2 + rateList.get(position) / 2 < 5) {
            extendLineWidth += 20;
            rate -= 0.05f;
        } else {
            extendLineWidth = 20;
            rate = 0.4f;
        }
        // 外延画折线
        float lineXPoint1 = (point.x - dip2px(leftMargin + ringOuterRidus)) * (1 + rate);
        float lineYPoint1 = (point.y - dip2px(topMargin + ringOuterRidus)) * (1 + rate);
        float[] floats = new float[8];
        floats[0] = point.x;
        floats[1] = point.y;
        floats[2] = dip2px(leftMargin + ringOuterRidus) + lineXPoint1;
        floats[3] = dip2px(topMargin + ringOuterRidus) + lineYPoint1;
        floats[4] = dip2px(leftMargin + ringOuterRidus) + lineXPoint1;
        floats[5] = dip2px(topMargin + ringOuterRidus) + lineYPoint1;
        if (point.x >= dip2px(leftMargin + ringOuterRidus)) {
            mPaint.setTextAlign(Paint.Align.LEFT);
            floats[6] = dip2px(leftMargin + ringOuterRidus) + lineXPoint1 + dip2px(extendLineWidth);
        } else {
            mPaint.setTextAlign(Paint.Align.RIGHT);
            floats[6] = dip2px(leftMargin + ringOuterRidus) + lineXPoint1 - dip2px(extendLineWidth);
        }
        floats[7] = dip2px(topMargin + ringOuterRidus) + lineYPoint1;
        mPaint.setColor(mRes.getColor(colorList.get(position)));
        canvas.drawLines(floats, mPaint);
        mPaint.setTextSize(dip2px(showRateSize));
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawText(rateList.get(position) + "%", floats[6], floats[7] + dip2px(showRateSize) / 3, mPaint);
//        canvas.drawText(titleList.get(position),floats[6], floats[7] + dip2px(showRateSize) / 3 + dip2px(showRateSize)  , mPaint);
        preRate = rateList.get(position);
    }

    List pointList = new ArrayList<>();
    List pointArcCenterList = new ArrayList<>();

    private void dealPoint(RectF rectF, float startAngle, float endAngle, List pointList) {
        Path orbit = new Path(); //通过Path类画一个90度(180—270)的内切圆弧路径
        orbit.addArc(rectF, startAngle, endAngle);
        PathMeasure measure = new PathMeasure(orbit, false);
        Log.e("路径的测量长度:", "" + measure.getLength());
        float[] coords = new float[]{0f, 0f}; //利用PathMeasure分别测量出各个点的坐标值coords
        int divisor = 1;
        measure.getPosTan(measure.getLength() / divisor, coords, null);
        Log.e("coords:", "x轴:" + coords[0] + " -- y轴:" + coords[1]);
        float x = coords[0];
        float y = coords[1];
        Point point = new Point(Math.round(x), Math.round(y));
        pointList.add(point);
    }

    private void drawOuter(Canvas canvas, int position) {
        mPaint.setTextSize(dip2px(showRateSize));
        canvas.drawCircle(circleCenterX, circleCenterY+position*circleCenterY, ringInnerRidus, mPaint);
        if (rateList != null) {
            endAngle = getAngle(rateList.get(position));
        } //        Log.e("preAngle:", "" + preAngle + "   endAngle:" + endAngle);
        canvas.drawArc(rectF, preAngle, endAngle, true, mPaint);
        canvas.drawText(titleList.get(position),circleCenterX+dip2px(showRateSize), circleCenterY+position*circleCenterY+dip2px(showRateSize)/2,mPaint);

        dealPoint(rectF, preAngle, endAngle, pointList);

        if (isShowRate) {
            drawArcCenterPoint(canvas, position);
        }
        preAngle = preAngle + endAngle;
    }

    private float preAngle = -90;
    private float endAngle = -90;

    /**
     * @param percent 百分比
     * @return
     */
    private float getAngle(float percent) {
        float a = 360f / 100f * percent;
        return a;
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public int dip2px(float dpValue) {
        return (int) (dpValue * dm.density + 0.5f);
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public int px2dip(float pxValue) {
        return (int) (pxValue / dm.density + 0.5f);
    }
}

xml中的使用




    
    
    
    

Activity中的使用

@BindView(R.id.radarview)
CustomRadarView radarView;//雷达图

@BindView(R.id.score_checklist)
HorizontalChartView scoreView;//水平柱状图

@BindView(R.id.histogramView)
HistogramView histogramView;//垂直柱状图

@BindView(R.id.ringView)
RingView ringView;//扇形图

@Override
protected int getLayoutId() {
    return R.layout.activity_about;
}

@Override
protected void initView() {

    List datas = new ArrayList<>();
    datas.add(100.0);//语法100分
    datas.add(80.0);//听力80分
    datas.add(50.0);//口语90分
    datas.add(70.0);//写作70分
    datas.add(60.0);//阅读理解60分

    List titles = new ArrayList<>();
    titles.add("语法1");
    titles.add("听力1");
    titles.add("口语1");
    titles.add("写作1");
    titles.add("阅读理解1");
    radarView.setValueTitle(titles);
    radarView.setData(datas);

    List barList = new ArrayList<>();
    barList.add(new HorizontalChartView.BarBean(18, "听力"));
    barList.add(new HorizontalChartView.BarBean(5, "语法"));
    barList.add(new HorizontalChartView.BarBean(7, "词法"));
    barList.add(new HorizontalChartView.BarBean(20, "阅读理解"));
    barList.add(new HorizontalChartView.BarBean(10, "说"));
    scoreView.setBarList(barList);

    ArrayList barLists = new ArrayList();
    for (int i = 0; i < 5; i++) {
        float ratio = (float) Math.random();
        int color = (int) (Color.GRAY * ratio);
        HistogramView.Bar bar = histogramView.new Bar(i, ratio, color, "测试" + i, "10" + i);
        barLists.add(bar);
    }
    histogramView.setBarLists(barLists);

    // 添加的是颜色
    List colorList = new ArrayList<>();
    colorList.add(R.color.pink);
    colorList.add(R.color.orange);
    colorList.add(R.color.yellow);
    colorList.add(R.color.blue); //  添加的是百分比

    List titleList=new ArrayList<>();
    titleList.add("粉色");
    titleList.add("橘色");
    titleList.add("黄色");
    titleList.add("蓝色");
    List rateList = new ArrayList<>();
    rateList.add(10f);
    rateList.add(5f);
    rateList.add(45f);
    rateList.add(40f);
    ringView.setData(titleList);
    ringView.setShow(colorList, rateList, false, true);

}

你可能感兴趣的:(Android 自定义雷达图 水平柱状图 垂直柱状图 扇形图)