MPAndroidChart 饼图 数据过少时文字重叠

Android 图表控件 MPAndroidChart 饼图 数值太少时Value文字重叠

在使用MPAndroidChart这个控件来画饼图时,当有几个数据都是占比非常少的时候,伸出来的文字会重叠在一块。 这个坑花了比较多的时间填 。本文地址:https://blog.csdn.net/aichu6610/article/details/108278760
在这里插入图片描述
在网上找了一圈,都是复制的别人的文章。按照文章思路做了之后,还是会有重叠,而且有些时候线条很丑。

这位大哥可能是原创的,有兴趣的可以点进去看下文章链接
在这里插入图片描述
在这里插入图片描述
也有可能是我的使用姿势不对,始终没有达到我想要的效果。一番折腾后,决定自己动手改造。改造后的效果图
在这里插入图片描述
在这里插入图片描述
基本思路是,找到绘制线条的类。PieChartRenderer。然后修改绘制的Y左边。记录已经绘制过的Y坐标,如果下一次要绘制的文本距离小于之前现有的距离。那就需要上下挪动位置了,如果在以前位置之上,那就减去一个文本的高度,拉大距离,如果在下面就增加,也是继续拉大距离。话不多说,直接上代码。

	
   
    /**
     * 计算离得最近的一个点位的y坐标
     *
     * @param leftRecordY 需要计算的数组
     * @param pt1y        当前的点
     * @return
     */
    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);
            }
        }
        LogUtils.i(TAG, "距离最近: " + minD);
        return rF;
    }

接下来是主要的操作代码,这个方法是PieChartRenderer文件的。需要使用源码的方式进行修改,如果是直接引入jar包的,需要参考上面链接老哥的方法进行修改文件。

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 = 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.3);//文本的高度

            float lineHeight = Utils.calcTextHeight(mValuePaint, "Q")
                    + Utils.convertDpToPixel(4f);

            ValueFormatter formatter = dataSet.getValueFormatter();

            int entryCount = dataSet.getEntryCount();

            boolean isUseValueColorForLineEnabled = dataSet.isUseValueColorForLineEnabled();
            int valueLineColor = dataSet.getValueLineColor();

            mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth()));

            final float sliceSpace = getSliceSpace(dataSet);

            MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset());
            iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x);
            iconsOffset.y = 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);
                LogUtils.e(TAG, entry.toString());

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

                final float sliceAngle = drawAngles[xIndex];
                final float sliceSpaceMiddleAngle = sliceSpace / (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 * Utils.FDEG2RAD);
                final float sliceYBase = (float) Math.sin(transformedAngle * 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;

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

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

                    LogUtils.i(TAG, "变换角度:" + transformedAngle);
                    if (transformedAngle % 360.0 >= 90.0 && transformedAngle % 360.0 <= 270.0) {

                        LogUtils.i(TAG, "<<<<<<<<<<<<<<<  变换前 :  labelPty : " + pt1y);

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

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

                        pt2x = pt1x - polyline2Width;
                        pt2y = pt1y;

                        mValuePaint.setTextAlign(Align.RIGHT);

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

                        labelPtx = pt2x - offset;
                        labelPty = pt2y;

                        leftRecordY[1][j] = pt1y;
                        LogUtils.i(TAG, "<<<<<<<<<<<<<<<<<<    labelPtx :" + labelPtx + " labelPty : " + labelPty);
                    } else {

                        LogUtils.i(TAG, ">>>>>>>>>>>>>>>>>>>>>  变换前 :  labelPty : " + pt1y);
                        float[] nearestPoint = minData(rightRecordY, pt1y);

                        rightRecordY[0][j] = pt1y;

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

                            }*/
                            pt1y = nearestPoint[1] + textHeight;
                        }

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

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

                        labelPtx = pt2x + offset;
                        labelPty = pt2y;

                        rightRecordY[1][j] = labelPty;
                        LogUtils.i(TAG, ">>>>>>>>>>>>>>>>>>>>>  变换后 :  labelPty : " + labelPty);
                    }

                    int lineColor = ColorTemplate.COLOR_NONE;

                    if (isUseValueColorForLineEnabled)
                        lineColor = dataSet.getColor(j);
                    else if (valueLineColor != ColorTemplate.COLOR_NONE)
                        lineColor = valueLineColor;

                    if (lineColor != ColorTemplate.COLOR_NONE) {
                        mValueLinePaint.setColor(lineColor);
                        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(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();
    }

以上就是修改文字重叠bug的全部代码了,算法写的有点烂,但是问题是解决了 ヾ(@⌒ー⌒@)ノ,有问题欢迎交流。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
要使用 MPAndroidChart 绘制饼图,你需要进行以下步骤: 1.在你的项目中添加 MPAndroidChart 依赖。你可以在 gradle 文件中添加以下代码: ``` implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' ``` 2.在你的布局文件中添加一个 PieChart 视图: ``` <com.github.mikephil.charting.charts.PieChart android:id="@+id/pie_chart" android:layout_width="match_parent" android:layout_height="match_parent"/> ``` 3.在你的 Activity 或 Fragment 中找到这个 PieChart 视图,并为它设置一些属性: ``` PieChart pieChart = findViewById(R.id.pie_chart); pieChart.setUsePercentValues(true); // 设置使用百分比值 pieChart.getDescription().setEnabled(false); // 隐藏描述 pieChart.setExtraOffsets(5, 10, 5, 5); // 设置间距 pieChart.setDrawHoleEnabled(true); // 绘制中间的空心圆 pieChart.setHoleColor(Color.WHITE); // 设置空心圆的颜色 pieChart.setTransparentCircleRadius(61f); // 设置透明圆的半径 pieChart.setHoleRadius(58f); // 设置空心圆的半径 pieChart.setRotationAngle(0); // 设置起始角度 pieChart.setRotationEnabled(true); // 可以旋转 pieChart.setHighlightPerTapEnabled(true); // 可以高亮显示 ``` 4.为饼图添加数据。首先创建一个 ArrayList<PieEntry>,然后将每个数据添加到其中: ``` ArrayList<PieEntry> entries = new ArrayList<>(); entries.add(new PieEntry(18.5f, "Green")); entries.add(new PieEntry(26.7f, "Yellow")); entries.add(new PieEntry(24.0f, "Red")); entries.add(new PieEntry(30.8f, "Blue")); ``` 5.将数据添加到 PieDataSet 中,并为数据集设置一些属性: ``` PieDataSet dataSet = new PieDataSet(entries, "Election Results"); dataSet.setSliceSpace(3f); // 设置每个扇形之间的间距 dataSet.setSelectionShift(5f); // 设置点击时的扇形半径 dataSet.setColors(ColorTemplate.JOYFUL_COLORS); // 设置颜色 ``` 6.创建一个 PieData 对象,将数据集传递给它: ``` PieData data = new PieData(dataSet); data.setValueTextSize(10f); // 设置文字大小 data.setValueTextColor(Color.YELLOW); // 设置文字颜色 ``` 7.将 PieData 对象设置到 PieChart 中: ``` pieChart.setData(data); pieChart.invalidate(); // 刷新图表 ``` 现在,你就可以显示你的饼图了。上面的代码只是一个简单的示例,你可以根据自己的需求调整图表的属性和样式。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值