自定义环形图表

自定义环形图表

前言: Android开发中,原生的控件只能满足大多时候的需求,对于一些特殊的样式需要自定义控件去实现,接下来实现一个环形的图标,需要用到的关键类Paint,RectF,Matrix,Path

自定义环形图表_第1张图片

基本思路

1.圆环
可以看到圆环有不通过的扇形区域,我们可以理解画两个圆弧然后首尾连接就可以形成该形状

Path path = new Path();
path.addArc(mMaxRect, startAngle, allData.get(i).getAngle());
path.arcTo(mMinRect, startAngle + allData.get(i).getAngle(), -allData.get(i).getAngle());
path.close();

2.画线
调用drawLine()方法

canvas.drawLine(startX, startY, endX, endY, mPaint);

3.画文字

换文字要注意基类线的位置

 mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(tmpText, endX + defaultS, endY + mTmpRect.height() / 2, mPaint);

所有代码

/**
 * Description:环形饼状图
 * Created by Buuu on 2018/04/27.
 */

public class CirclePieChart extends View {
    private List allData;
    private Paint mPaint;
    private RectF mMinRect;
    private RectF mMaxRect;
    private Matrix mMatrix;
    private List mPathsList;
    private int mWH;
    private int mMaxWH;
    private int defaultSpace = 250;//周围宽度
    private float startAngle = 0f; //初始角度
    private float lineLength = 50; //指示线的长度

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

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

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

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mMatrix.reset();
        if (allData != null && !allData.isEmpty()) {
            mMaxWH = Math.min(w, h);
            mWH = Math.min(w, h) - defaultSpace;
            //通过调整内圈或外圈的大小 调整宽度
            mMaxRect = new RectF(-mWH / 2, -mWH / 2, mWH / 2, mWH / 2);
            mMinRect = new RectF(-mWH / 4, -mWH / 4, mWH / 4, mWH / 4);
            //画环形图形,如需点击事件,根据需求添加
            for (int i = 0; i < allData.size(); i++) {
                Path path = new Path();
                path.addArc(mMaxRect, startAngle, allData.get(i).getAngle());
                path.arcTo(mMinRect, startAngle + allData.get(i).getAngle(), -allData.get(i).getAngle());
                path.close();
                startAngle+=allData.get(i).getAngle();
                mPathsList.add(path);
            }
        }
    }

    private void initData() {
        mPathsList = new ArrayList<>();
        mPaint = new Paint();
        mPaint.setTextSize(28);
        mPaint.setColor(Color.parseColor("#3300f0ff"));
        mPaint.setTextAlign(Paint.Align.LEFT);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(3);
        mMatrix = new Matrix();
    }

    private Rect mTmpRect = new Rect();
    private float defaultS = 10F;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(mMaxWH / 2, mMaxWH / 2);
        if (mMatrix.isIdentity()) {
            canvas.getMatrix().invert(mMatrix);
        }
        for (int i = 0; i < mPathsList.size(); i++) {
            mPaint.setColor(allData.get(i).getColor());
            canvas.drawPath(mPathsList.get(i), mPaint);
        }
        float childStartAngle = startAngle;
        //画线和字
        for (int i = 0; i < allData.size(); i++) {
            double cos = Math.cos(Math.toRadians(childStartAngle + (allData.get(i).getAngle() ) / 2));
            double sin = Math.sin(Math.toRadians(childStartAngle + (allData.get(i).getAngle() ) / 2));
            float startX = (float) (cos * mWH) / 2;
            float endX = (float) (lineLength * cos + startX);
            float startY = (float) (sin * mWH) / 2;
            float endY = (float) (lineLength * sin + startY);
            mPaint.setColor(allData.get(i).getColor());
            canvas.drawLine(startX, startY, endX, endY, mPaint);
            String tmpText = (int) Math.ceil(allData.get(i).getAngle()*100/360) +"%" ;
            mPaint.getTextBounds(tmpText, 0, tmpText.length(), mTmpRect);

            if (endY == 0 && endX > 0) {// x轴 正方向
                mPaint.setTextAlign(Paint.Align.LEFT);
                canvas.drawText(tmpText, endX + defaultS, endY + mTmpRect.height() / 2, mPaint);
            } else if (endX > 0 && endY > 0 && endX >= endY) {//x轴 正方向 0
                mPaint.setTextAlign(Paint.Align.LEFT);
                canvas.drawText(tmpText, endX + defaultS, endY + mTmpRect.height() / 2, mPaint);
            } else if (endX > 0 && endY > 0 && endX < endY) {//x轴 正方向 45
                mPaint.setTextAlign(Paint.Align.CENTER);
                canvas.drawText(tmpText, endX, endY + mTmpRect.height() + defaultS, mPaint);
            } else if (endX == 0 && endY > 0) { //y轴正方向
                mPaint.setTextAlign(Paint.Align.CENTER);
                canvas.drawText(tmpText, endX, endY + mTmpRect.height() + defaultS, mPaint);
            } else if (endX < 0 && endY > 0 && Math.abs(endX) <= Math.abs(endY)) {//y轴正方向 90
                mPaint.setTextAlign(Paint.Align.CENTER);
                canvas.drawText(tmpText, endX, endY + mTmpRect.height() + defaultS, mPaint);
            } else if (endX < 0 && endY > 0 && Math.abs(endX) > Math.abs(endY)) {//y轴正方向 135
                mPaint.setTextAlign(Paint.Align.RIGHT);
                canvas.drawText(tmpText, endX - defaultS, endY + mTmpRect.height() / 2, mPaint);
            } else if (endX < 0 && endY == 0) {//x轴负反向
                mPaint.setTextAlign(Paint.Align.RIGHT);
                canvas.drawText(tmpText, endX - defaultS, endY + mTmpRect.height() / 2, mPaint);
            } else if (endX < 0 && endY < 0 && Math.abs(endX) >= Math.abs(endY)) {//x轴负反向 180
                mPaint.setTextAlign(Paint.Align.RIGHT);
                canvas.drawText(tmpText, endX - defaultS, endY + mTmpRect.height() / 2, mPaint);
            } else if (endX < 0 && endY < 0 && Math.abs(endX) < Math.abs(endY)) {//x轴负反向 225
                mPaint.setTextAlign(Paint.Align.CENTER);
                canvas.drawText(tmpText, endX, endY - defaultS, mPaint);
            } else if (endX == 0 && endY < 0) {//y轴负方向
                mPaint.setTextAlign(Paint.Align.CENTER);
                canvas.drawText(tmpText, endX, endY - defaultS, mPaint);
            } else if (endX > 0 && endY < 0 && Math.abs(endX) <= Math.abs(endY)) {//y轴负方向 270
                mPaint.setTextAlign(Paint.Align.CENTER);
                canvas.drawText(tmpText, endX, endY - defaultS, mPaint);
            } else if (endX > 0 && endY < 0 && Math.abs(endX) > Math.abs(endY)) {//y轴负方向 315
                mPaint.setTextAlign(Paint.Align.LEFT);
                canvas.drawText(tmpText, endX + defaultS, endY + mTmpRect.height() / 2, mPaint);
            } else {//其他
                mPaint.setTextAlign(Paint.Align.CENTER);
                canvas.drawText(tmpText, endX + defaultS, endY + defaultS, mPaint);
            }
            childStartAngle += allData.get(i).getAngle();
        }
    }

    /**
     * 设置数据
     *
     * @param allDatas PieChartPart
     */
    public void setData(List allDatas) {
        this.allData = allDatas;


        int numericalSum = 0;
        int tempAngle;

        int standardAngle = 360;

        for (int i = 0; i < allData.size(); i++) {
            numericalSum+=allData.get(i).getNumerical();
        }
        for (int i = 0; i < allData.size(); i++) {
            tempAngle = (int) (allData.get(i).getNumerical() * 360 / numericalSum);

            if(i>1){
                tempAngle +=1;
            }

            if (allData.size()-1 == i){
                tempAngle = standardAngle;
            }else {
                standardAngle -= tempAngle;
            }
            allData.get(i).setAngle(tempAngle);

        }

        invalidate();
    }
}
//PieChartPart
public class PieChartPart {
    private int color;
    private float angle;
    private float numerical;

    public PieChartPart() {
    }

    public PieChartPart(int color, float numerical) {
        this.color = color;
        this.numerical = numerical;
    }

    public PieChartPart(int color, float numerical,float angle) {
        this.color = color;
        this.numerical = numerical;
        this.angle = angle;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public float getAngle() {
        return angle;
    }

    public void setAngle(float angle) {
        this.angle = angle;
    }

    public float getNumerical() {
        return numerical;
    }

    public void setNumerical(int numerical) {
        this.numerical = numerical;
    }
}

基本思路

主要用到Path类,为更好的理解可以着重看一下该类,哦,对了,如果涉及到点击事件的话可以采用region

你可能感兴趣的:(自定义控件)