Android自定义View实现饼状图

1.简单实现饼状图效果
1.1、创建一个实体类存储数据(数值、颜色、开始角度、结束角度)

public class MySector {
    private float value;
    private String colour;
    private float startAngle;
    private float endAngle;

    public MySector(Builder builder){
        this.value = builder.value > 0 ? builder.value : 1; //防止分母为零
        this.colour = builder.colour;
        this.startAngle = builder.startAngle;
        this.endAngle = builder.endAngle;
    }

    public float getValue() {
        return value;
    }

    public void setValue(float value) {
        this.value = value;
    }

    public String getColour() {
        return colour;
    }

    public void setColour(String colour) {
        this.colour = colour;
    }

    public float getStartAngle() {
        return startAngle;
    }

    public void setStartAngle(float startAngle) {
        this.startAngle = startAngle;
    }

    public float getEndAngle() {
        return endAngle;
    }

    public void setEndAngle(float endAngle) {
        this.endAngle = endAngle;
    }

    static class Builder{
        private float value;
        private String colour;
        private float startAngle;
        private float endAngle;

        public MySector.Builder value(float value){
            this.value = value;
            return this;
        }
        public MySector.Builder colour(String colour){
            this.colour = colour;
            return this;
        }

        public MySector.Builder startAngle(float startAngle){
            this.startAngle = startAngle;
            return this;
        }

        public MySector.Builder endAngle(float endAngle){
            this.endAngle = endAngle;
            return this;
        }

        public MySector build(){
            return new MySector(this);
        }
    }
}

1.2、自定义View画饼状图扇形
(1)创建init方法初始化数据和画笔
(2)创建setRadius方法设置饼状图大小
(3)创建setMySector方法设置数据更新界面
(4)重写onDraw方法

public class MyChart extends View {
    private List<MySector> mySectors;
    private Paint paint;
    private float centerX;
    private float centerY;
    private float Radius;
    public MyChart(Context context) {
        super(context);
        init();
    }

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

    public MyChart(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mySectors = new ArrayList<>();
        paint = new Paint();
        paint.setTextSize(DensityUtil.px2dip(getContext(), 150));
        paint.setAntiAlias(true);
    }

    public void setRadius(float radius) {
        this.Radius = radius;
    }

    public void setPieEntries(List<MySector> mySectors) {
        this.mySectors = mySectors;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //计算数值总值
        int total = 0;
        for (int i = 0; i < mySectors.size(); i++) {
            total += mySectors.get(i).getValue();
        }
        //圆心和半径
        centerX = getPivotX();
        centerY = getPivotY();
        if (Radius == 0) {
            Radius = (getWidth() > getHeight() ? getHeight() / 2 : getWidth() / 2);
        }
        //开始角度设置为0
        float startC = 0;
        for (int i = 0; i < mySectors.size(); i++) {
            //计算当前扇形角度
            float sweep = 360 * (mySectors.get(i).getValue() / total);
            //设置当前扇形颜色
            int mDayColor = Color.parseColor(mySectors.get(i).getColour());
            paint.setColor(mDayColor);
            //画扇形的方法
            RectF rectF = new RectF(centerX - Radius, centerY - Radius, centerX + Radius, centerY + Radius);
            canvas.drawArc(rectF, startC, sweep, true, paint);
            //将每个扇形的起始角度和结束角度放入对应的对象
            mySectors.get(i).setStartAngle(startC);
            mySectors.get(i).setEndAngle(startC + sweep);
            //将当前扇形的结束角度作为下一个扇形的开始角度
            startC += sweep;
        }
    }
}

1.3、xml文件代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.chx.piechartdemo.MyChart
        
        android:id="@+id/myChart"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:background="@android:color/white" />
</RelativeLayout>

1.4、Activity中使用代码

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myChart = (MyChart) findViewById(R.id.myChart);
        myChart.setRadius(DensityUtil.px2dip(this, 800));
        List<MySector> mySectorList = new ArrayList<>();
        MySector.Builder builder = new MySector.Builder();
        mySectorList.add(builder.value(1).colour("#FFA500").build());
        mySectorList.add(builder.value(2).colour("#23BF70").build());
        mySectorList.add(builder.value(3).colour("#7155EC").build());
        mySectorList.add(builder.value(4).colour("#B09EFF").build());
        mySectorList.add(builder.value(5).colour("#00A2D0").build());
        mySectorList.add(builder.value(6).colour("#41DBCB").build());
        myChart.setMySector(mySectorList);
    }

2.四周带百分比显示的饼状图
2.1、修改onDraw中的代码(代码中有详细注解)

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //计算数值总值
        int total = 0;
        for (int i = 0; i < mySectors.size(); i++) {
            total += mySectors.get(i).getValue();
        }
        //圆心和半径
        centerX = getPivotX();
        centerY = getPivotY();
        if (Radius == 0) {
            Radius = (getWidth() > getHeight() ? getHeight() / 2 : getWidth() / 2);
        }
        //开始角度设置为0
        float startC = 0;
        for (int i = 0; i < mySectors.size(); i++) {
            //计算当前扇形角度
            float sweep = 360 * (mySectors.get(i).getValue() / total);
            //设置当前扇形颜色
            int mDayColor = Color.parseColor(mySectors.get(i).getColour());
            paint.setColor(mDayColor);
            //画扇形的方法
            RectF rectF = new RectF(centerX - Radius, centerY - Radius, centerX + Radius, centerY + Radius);
            canvas.drawArc(rectF, startC, sweep, true, paint);
            //下面是画扇形外围的短线和百分数值。
            float arcCenterC = startC + sweep / 2; //当前扇形弧线的中间点和圆心的连线与起始角度的夹角
            float arcCenterX = 0;  //当前扇形弧线的中间点的坐标x以此点作为短线的起点
            float arcCenterY = 0;  //当前扇形弧线的中间点的坐标y
            float arcCenterX2 = 0; //这两个点作为短线的结束点
            float arcCenterY2 = 0;
            //百分百数字的格式
            DecimalFormat numberFormat = new DecimalFormat("00.00");
            paint.setColor(Color.BLACK);
            //利用三角函数来求出每个短线的起始点和结束点,并画出短线和百分比。
            //具体的计算方法看下面图示介绍
            if (arcCenterC >= 0 && arcCenterC < 90) {
                arcCenterX = (float) (centerX + Radius * Math.cos(arcCenterC * Math.PI / 180));
                arcCenterY = (float) (centerY + Radius * Math.sin(arcCenterC * Math.PI / 180));
                arcCenterX2 = (float) (arcCenterX + DensityUtils.dp2px(getContext(), 10) * Math.cos(arcCenterC * Math.PI / 180));
                arcCenterY2 = (float) (arcCenterY + DensityUtils.dp2px(getContext(), 10) * Math.sin(arcCenterC * Math.PI / 180));
                canvas.drawLine(arcCenterX, arcCenterY, arcCenterX2, arcCenterY2, paint);
                canvas.drawText(numberFormat.format(mySectors.get(i).getValue() / total * 100) + "%", arcCenterX2, arcCenterY2 + paint.getTextSize() / 2, paint);
            } else if (arcCenterC >= 90 && arcCenterC < 180) {
                arcCenterC = 180 - arcCenterC;
                arcCenterX = (float) (centerX - Radius * Math.cos(arcCenterC * Math.PI / 180));
                arcCenterY = (float) (centerY + Radius * Math.sin(arcCenterC * Math.PI / 180));
                arcCenterX2 = (float) (arcCenterX - DensityUtils.dp2px(getContext(), 10) * Math.cos(arcCenterC * Math.PI / 180));
                arcCenterY2 = (float) (arcCenterY + DensityUtils.dp2px(getContext(), 10) * Math.sin(arcCenterC * Math.PI / 180));
                canvas.drawLine(arcCenterX, arcCenterY, arcCenterX2, arcCenterY2, paint);
                canvas.drawText(numberFormat.format(mySectors.get(i).getValue() / total * 100) + "%", (float) (arcCenterX2 - paint.getTextSize() * 3.5), arcCenterY2 + paint.getTextSize() / 2, paint);
            } else if (arcCenterC >= 180 && arcCenterC < 270) {
                arcCenterC = 270 - arcCenterC;
                arcCenterX = (float) (centerX - Radius * Math.sin(arcCenterC * Math.PI / 180));
                arcCenterY = (float) (centerY - Radius * Math.cos(arcCenterC * Math.PI / 180));
                arcCenterX2 = (float) (arcCenterX - DensityUtils.dp2px(getContext(), 10) * Math.sin(arcCenterC * Math.PI / 180));
                arcCenterY2 = (float) (arcCenterY - DensityUtils.dp2px(getContext(), 10) * Math.cos(arcCenterC * Math.PI / 180));
                canvas.drawLine(arcCenterX, arcCenterY, arcCenterX2, arcCenterY2, paint);
                canvas.drawText(numberFormat.format(mySectors.get(i).getValue() / total * 100) + "%", (float) (arcCenterX2 - paint.getTextSize() * 3.5), arcCenterY2, paint);
            } else if (arcCenterC >= 270 && arcCenterC < 360) {
                arcCenterC = 360 - arcCenterC;
                arcCenterX = (float) (centerX + Radius * Math.cos(arcCenterC * Math.PI / 180));
                arcCenterY = (float) (centerY - Radius * Math.sin(arcCenterC * Math.PI / 180));
                arcCenterX2 = (float) (arcCenterX + DensityUtils.dp2px(getContext(), 10) * Math.cos(arcCenterC * Math.PI / 180));
                arcCenterY2 = (float) (arcCenterY - DensityUtils.dp2px(getContext(), 10) * Math.sin(arcCenterC * Math.PI / 180));
                canvas.drawLine(arcCenterX, arcCenterY, arcCenterX2, arcCenterY2, paint);
                canvas.drawText(numberFormat.format(mySectors.get(i).getValue() / total * 100) + "%", arcCenterX2, arcCenterY2, paint);
            }
            //将每个扇形的起始角度和结束角度放入对应的对象
            mySectors.get(i).setStartAngle(startC);
            mySectors.get(i).setEndAngle(startC + sweep);
            //将当前扇形的结束角度作为下一个扇形的开始角度
            startC += sweep;
        }
    }

3.带点击效果的饼状图(更新中…)

    原文作者:kjy_112233
    原文地址: https://www.jianshu.com/p/88b111a35022
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞