Android 开发中开源库 MPAndroidChart使用总结——PieChart饼图和BarChart柱状图

首先,引入 MPAndroidChart 依赖

implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'

xml代码

其次,在xml中使用MPAndroidChart库引用的实例:

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/left_titlebar"
        android:background="@mipmap/bg_home_dh">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:ignore="ExtraText">

            <lonan.net.guangzhou.ui.user.MyPieChart
                android:id="@+id/pieChart"
                android:layout_width="match_parent"
                android:layout_height="350dp"
                android:layout_marginTop="@dimen/dp_20"
                android:layout_below="@id/button36"/>

            <com.github.mikephil.charting.charts.BarChart
                android:id="@+id/barChart"
                android:layout_width="match_parent"
                android:layout_height="350dp"
                android:layout_below="@id/pieChart" />


        </RelativeLayout>
    </androidx.core.widget.NestedScrollView>

数据绑定

    @BindView(R.id.pieChart)
    MyPieChart pieChart;
    @BindView(R.id.barChart)
    BarChart barChart;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FullActivityUtils.full(this);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        setContentView(R.layout.demo);
        MyApplication.addActivity(this);
        titlebar2.getCenterTextView().setText("****");
//自己定义一个getData方法,方法执行结束后,执行图表渲染的方法,将数据绑定上去
        getData();


    }

图表代码

PieChart饼图和BarChart柱状图:

    private void setPieChartData() {
        for (int i = 0; i < itemNumber; i++) {
            yValues.add(new PieEntry(itemValue.get(i), itemName.get(i)));
        }
        PieDataSet pieDataSet = new PieDataSet(yValues, "");
        pieDataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);//value外部展示


        PieData pieData = new PieData(pieDataSet);
        pieChart.setData(pieData);
        pieChart.setExtraOffsets(40f, 0f, 40f, 0f);
        //设置各个数据的颜色
        pieDataSet.setColors(ColorTemplate.COLORFUL_COLORS);
        //实体扇形的空心圆的半径   设置成0时就是一个圆 而不是一个环
        pieChart.setHoleRadius(30);
        //中间半透明白色圆的半径    设置成0时就是隐藏
        pieChart.setTransparentCircleRadius(30);
        //设置中心圆的颜色
        pieChart.setHoleColor(Color.CYAN);
        //设置中心部分的字  (一般中间白色圆不隐藏的情况下才设置)
        pieChart.setCenterText("定义在圆环中心的标题");
        //设置中心字的字体颜色
        pieChart.setCenterTextColor(Color.BLACK);
        //设置描述的字体大小
        pieChart.setEntryLabelTextSize(12);
        pieChart.setEntryLabelColor(Color.WHITE);
        Description description = pieChart.getDescription();
        description.setText("定义你自己的标题");
        //设置数据的字体大小
        pieDataSet.setValueTextSize(12);
        //设置描述的位置
        pieDataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);
        pieDataSet.setValueLinePart1Length(1.2f);//设置描述连接线长度
        //设置数据的位置
        pieDataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);
        pieDataSet.setValueLinePart2Length(1.2f);//设置数据连接线长度
        //设置两根连接线的颜色
        pieDataSet.setValueLineColor(Color.WHITE);

        //对于右下角一串字母的操作
        pieChart.getDescription().setEnabled(false);                  //是否显示右下角描述
        //获取图例,是否显示图例
        pieChart.getLegend().setEnabled(false);


        pieChart.animateY(1000); //在Y轴的动画  参数是动画执行时间 毫秒为单位


    }

    private void setBarChartData() {
        ArrayList<BarEntry> barEntries = new ArrayList<>();
        for (int i = 0; i < itemNumber; i++) {
            barEntries.add(new BarEntry(i, itemValue.get(i)));
        }

        BarDataSet barDataSet = new BarDataSet(barEntries, "");
        barDataSet.setValueTextColor(Color.RED);
        barDataSet.setValueTextSize(15f);
        barDataSet.setColor(Color.parseColor("#03A9F4"));

        BarData barData = new BarData(barDataSet);
        barData.setBarWidth(0.3f);

        //设置控件之间的间距
        barChart.setExtraOffsets(20, 20, 20, 20);
        //获取XAxis 获取XAxis  setDrawGridLines:设置绘图网格线
        barChart.getXAxis().setDrawGridLines(false);
        //获取描述,是否显示右下角描述
        barChart.getDescription().setEnabled(false);
        //获取图例,是否显示图例
        barChart.getLegend().setEnabled(false);

        barChart.setData(barData);
        barChart.getDescription().setText("定义你自己的标题");
        barChart.getDescription().setEnabled(true);

        XAxis xAxis = barChart.getXAxis();
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
        xAxis.setAxisMinimum(0f);
        xAxis.setAxisMaximum(itemNumber);
        xAxis.setLabelCount(itemNumber);
        xAxis.setGranularity(1);
        xAxis.setTextColor(Color.BLACK);
        xAxis.setLabelRotationAngle(45f);
        xAxis.setLabelCount(itemNumber, true);
        xAxis.setValueFormatter(new ValueFormatter() {
            @Override
            public String getFormattedValue(float value) {
                int index = (int) value;
                if (index >= 0 && index < itemNumber) {
                    return itemName.get(index);
                } else {
                    return "";
                }
            }
        });

        YAxis axisRight = barChart.getAxisRight();
        axisRight.setEnabled(false);

        YAxis axisLeft = barChart.getAxisLeft();
        axisLeft.setTextColor(Color.BLACK);
        axisLeft.setAxisMaximum(250f); // 设置适当的最大值
        axisLeft.setAxisMinimum(0f);
        axisLeft.setTextSize(15f);

        barChart.animateXY(1000, 1000);
        barChart.invalidate();
    }

使用中遇到的问题

对图表的样式、大小,渲染效果需要调整的可以考虑重写库的渲染方法。比如,我遇到的有一个问题,数据太多但是MPAndroidChart 饼状图太小,出现了文字重叠问题,以下提供一个解决问题的思路:可以尝试根据自己的需求重写PieChartRenderer。

public class MyPieChart extends PieChart {

    public MyPieChart(Context context) {
        super(context);
    }

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

    public MyPieChart(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void init() {
        super.init();
        //此处把mRenderer替换成我们自己的PieChartRenderer
        mRenderer = new PieChartCustomRendederer(this, mAnimator, mViewPortHandler);
    }
}

public class PieChartCustomRendederer extends PieChartRenderer {
    private Paint mEntryLabelsPaint = new Paint();

    public PieChartCustomRendederer(PieChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
        super(chart, animator, viewPortHandler);
    }

    private float[] minData(float leftRecordY[][], float pt1y) {
        //计算出离的最近的
        List<Float> bigD = new ArrayList<>();
        //变换前的数据
        List<Float> nearestlist = new ArrayList<>();
        //变换后的数据
        List<Float> nearestlistCopy = new ArrayList<>();
        for (int k = 0; k < leftRecordY[0].length; k++) {
            if (leftRecordY[0][k] != 0) {
                bigD.add(Math.abs(leftRecordY[0][k] - pt1y));
                nearestlist.add(leftRecordY[0][k]);
                nearestlistCopy.add(leftRecordY[1][k]);
            }
        }
        // 距离最近的点,数值
        float[] rF = new float[2];
        if (bigD.size() == 0) {
            return rF;
        }

        float minD = bigD.get(0);
        rF[0] = nearestlist.get(0);
        rF[1] = nearestlistCopy.get(0);

        for (int g = 0; g < bigD.size(); g++) {
            if (bigD.get(g) < minD) {
                minD = bigD.get(g);
                rF[0] = nearestlist.get(g);
                rF[1] = nearestlistCopy.get(g);
            }
        }
        return rF;
    }

    private String text = "2.0%";

    @Override
    public void drawValues(Canvas c) {

        MPPointF center = mChart.getCenterCircleBox();

        // get whole the radius
        float radius = mChart.getRadius();
        float rotationAngle = mChart.getRotationAngle();
        float[] drawAngles = mChart.getDrawAngles();
        float[] absoluteAngles = mChart.getAbsoluteAngles();

        float phaseX = mAnimator.getPhaseX();
        float phaseY = mAnimator.getPhaseY();

        final float roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f;
        final float holeRadiusPercent = mChart.getHoleRadius() / 100.f;
        float labelRadiusOffset = radius / 10f * 3.6f;

        if (mChart.isDrawHoleEnabled()) {
            labelRadiusOffset = (radius - (radius * holeRadiusPercent)) / 2f;

            if (!mChart.isDrawSlicesUnderHoleEnabled() && mChart.isDrawRoundedSlicesEnabled()) {
                // Add curved circle slice and spacing to rotation angle, so that it sits nicely inside
                rotationAngle += roundedRadius * 360 / (Math.PI * 2 * radius);
            }
        }

        final float labelRadius = radius - labelRadiusOffset;

        PieData data = mChart.getData();
        List<IPieDataSet> dataSets = data.getDataSets();

        float yValueSum = data.getYValueSum();

        boolean drawEntryLabels = mChart.isDrawEntryLabelsEnabled();

        float angle;
        int xIndex = 0;

        c.save();

        float offset = com.github.mikephil.charting.utils.Utils.convertDpToPixel(5.f);

        for (int i = 0; i < dataSets.size(); i++) {

            IPieDataSet dataSet = dataSets.get(i);

            final boolean drawValues = dataSet.isDrawValuesEnabled();

            if (!drawValues && !drawEntryLabels)
                continue;

            final PieDataSet.ValuePosition xValuePosition = dataSet.getXValuePosition();
            final PieDataSet.ValuePosition yValuePosition = dataSet.getYValuePosition();

            // apply the text-styling defined by the DataSet
            applyValueTextStyle(dataSet);
            Rect rect = new Rect();
            mValuePaint.getTextBounds(text, 0, text.length(), rect);
            mValuePaint.setColor(dataSet.getColor(i));
            int textHeight = (int) (rect.height() * 1.4f);//文本的高度
            float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") + Utils.convertDpToPixel(4f);

            ValueFormatter formatter = dataSet.getValueFormatter();

            int entryCount = dataSet.getEntryCount();

            mValueLinePaint.setColor(dataSet.getValueLineColor());
            mValueLinePaint.setStrokeWidth(com.github.mikephil.charting.utils.Utils.convertDpToPixel(dataSet.getValueLineWidth()));

            final float sliceSpace = getSliceSpace(dataSet);

            MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset());
            iconsOffset.x = com.github.mikephil.charting.utils.Utils.convertDpToPixel(iconsOffset.x);
            iconsOffset.y = com.github.mikephil.charting.utils.Utils.convertDpToPixel(iconsOffset.y);
            //第一个存以前的,第二个存变换位置后的
            float leftRecordY[][] = new float[2][entryCount];
            float rightRecordY[][] = new float[2][entryCount];

            for (int j = 0; j < entryCount; j++) {

                PieEntry entry = dataSet.getEntryForIndex(j);

                if (xIndex == 0)
                    angle = 0.f;
                else
                    angle = absoluteAngles[xIndex - 1] * phaseX;

                final float sliceAngle = drawAngles[xIndex];
                final float sliceSpaceMiddleAngle = sliceSpace / (com.github.mikephil.charting.utils.Utils.FDEG2RAD * labelRadius);

                // offset needed to center the drawn text in the slice
                final float angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2.f) / 2.f;

                angle = angle + angleOffset;

                final float transformedAngle = rotationAngle + angle * phaseY;

                float value = mChart.isUsePercentValuesEnabled() ? entry.getY()
                        / yValueSum * 100f : entry.getY();
                String formattedValue = formatter.getPieLabel(value, entry);
                String entryLabel = entry.getLabel();

                final float sliceXBase = (float) Math.cos(transformedAngle * com.github.mikephil.charting.utils.Utils.FDEG2RAD);
                final float sliceYBase = (float) Math.sin(transformedAngle * com.github.mikephil.charting.utils.Utils.FDEG2RAD);

                final boolean drawXOutside = drawEntryLabels &&
                        xValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE;
                final boolean drawYOutside = drawValues &&
                        yValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE;
                final boolean drawXInside = drawEntryLabels &&
                        xValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE;
                final boolean drawYInside = drawValues &&
                        yValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE;

                if (drawXOutside || drawYOutside) {

                    final float valueLineLength1 = dataSet.getValueLinePart1Length();
                    final float valueLineLength2 = dataSet.getValueLinePart2Length();
                    final float valueLinePart1OffsetPercentage = dataSet.getValueLinePart1OffsetPercentage() / 100.f;

                    float pt2x, pt2y;
                    float labelPtx, labelPty;

                    float line1Radius;

                    if (mChart.isDrawHoleEnabled())
                        line1Radius = (radius - (radius * holeRadiusPercent))
                                * valueLinePart1OffsetPercentage
                                + (radius * holeRadiusPercent);
                    else
                        line1Radius = radius * valueLinePart1OffsetPercentage;

                    final float polyline2Width = dataSet.isValueLineVariableLength()
                            ? labelRadius * valueLineLength2 * (float) Math.abs(Math.sin(
                            transformedAngle * Utils.FDEG2RAD))
                            : labelRadius * valueLineLength2;

                    float pt0x = line1Radius * sliceXBase + center.x;
                    float pt0y = line1Radius * sliceYBase + center.y;

                    float pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x;
                    float pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y;

                    if (transformedAngle % 360.0 >= 90.0 && transformedAngle % 360.0 <= 270.0) {

                        float nearestPoint[] = minData(leftRecordY, pt1y);

                        leftRecordY[0][j] = pt1y;
                        //判断是否需要挪位置
                        if (nearestPoint[0] != 0 && Math.abs(nearestPoint[0] - pt1y) < (textHeight + lineHeight)) {
                            pt1y = nearestPoint[1] - textHeight;
                        }
                        pt2x = pt1x - polyline2Width;
                        pt2y = pt1y;

                        mValuePaint.setTextAlign(Paint.Align.RIGHT);

                        if (drawXOutside)
                            mEntryLabelsPaint.setTextAlign(Paint.Align.RIGHT);

                        labelPtx = pt2x - offset;
                        labelPty = pt2y;
                        leftRecordY[1][j] = pt1y;
                    } else {
                        float[] nearestPoint = minData(rightRecordY, pt1y);

                        rightRecordY[0][j] = pt1y;

                        //判断是否需要挪位置
                        if (nearestPoint[0] != 0 && Math.abs(nearestPoint[0] - pt1y) < (textHeight + lineHeight)) {
                            pt1y = nearestPoint[1] + textHeight;
                        }

                        pt2x = pt1x + polyline2Width;
                        pt2y = pt1y;
                        mValuePaint.setTextAlign(Paint.Align.LEFT);

                        if (drawXOutside)
                            mEntryLabelsPaint.setTextAlign(Paint.Align.LEFT);

                        labelPtx = pt2x + offset;
                        labelPty = pt2y;
                        rightRecordY[1][j] = labelPty;
                    }

                    if (dataSet.getValueLineColor() != ColorTemplate.COLOR_NONE) {

                        if (dataSet.isUsingSliceColorAsValueLineColor()) {
                            mValueLinePaint.setColor(dataSet.getColor(j));
                        }

                        c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint);
                        c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint);
                    }

                    // draw everything, depending on settings
                    if (drawXOutside && drawYOutside) {

                        drawValue(c, formattedValue, labelPtx, labelPty, dataSet.getValueTextColor(j));

                        if (j < data.getEntryCount() && entryLabel != null) {
                            drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight);
                        }

                    } else if (drawXOutside) {
                        if (j < data.getEntryCount() && entryLabel != null) {
                            drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight / 2.f);
                        }
                    } else if (drawYOutside) {

                        drawValue(c, formattedValue, labelPtx, labelPty + lineHeight / 2.f, dataSet.getValueTextColor(j));
                    }
                }

                if (drawXInside || drawYInside) {
                    // calculate the text position
                    float x = labelRadius * sliceXBase + center.x;
                    float y = labelRadius * sliceYBase + center.y;

                    mValuePaint.setTextAlign(Paint.Align.CENTER);

                    // draw everything, depending on settings
                    if (drawXInside && drawYInside) {

                        drawValue(c, formattedValue, x, y, dataSet.getValueTextColor(j));

                        if (j < data.getEntryCount() && entryLabel != null) {
                            drawEntryLabel(c, entryLabel, x, y + lineHeight);
                        }

                    } else if (drawXInside) {
                        if (j < data.getEntryCount() && entryLabel != null) {
                            drawEntryLabel(c, entryLabel, x, y + lineHeight / 2f);
                        }
                    } else if (drawYInside) {
                        drawValue(c, formattedValue, x, y + lineHeight / 2f, dataSet.getValueTextColor(j));
                    }
                }

                if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {

                    Drawable icon = entry.getIcon();


                    float x = (labelRadius + iconsOffset.y) * sliceXBase + center.x;
                    float y = (labelRadius + iconsOffset.y) * sliceYBase + center.y;
                    y += iconsOffset.x;

                    Utils.drawImage(
                            c,
                            icon,
                            (int) x,
                            (int) y,
                            icon.getIntrinsicWidth(),
                            icon.getIntrinsicHeight());
                }

                xIndex++;
            }

            MPPointF.recycleInstance(iconsOffset);
        }
        MPPointF.recycleInstance(center);
        c.restore();
    }
}

总结

代码可以直接复制过去用,注意依赖引入的版本,版本不一致,代码可能有报错
有问题的同学欢迎留言
如果帮到你了,不妨点个赞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值