彩色 Horizo​​ntalBarChart 的 MPAndroidchart 圆形边缘

MPAndroidchart rounded edges for colorized HorizontalBarChart

我想使用 MPAndroidChart 制作一个带有圆边的彩色 Horizo​​ntalBarChart。我正在尝试为我的图表更改 Horizo​​ntalBarChartRenderer。这是我的代码:

HorizontalBarChart初始化:

List<Float> values = new ArrayList<>();
List<Integer> colors = new ArrayList<>();
// initializing

float[] valuesArray = new float[values.size()];
for (int i = 0; i < values.size(); i++) {
    valuesArray[i] = values.get(i);
}
List<BarEntry> yValues = valuesArray.length <= 0
    ? Collections.<BarEntry>emptyList()
    : Collections.singletonList(new BarEntry(valuesArray, 0));
BarDataSet barDataSet = new BarDataSet(yValues, "");
barDataSet.setColors(colors);
barDataSet.setValueFormatter(new ValueFormatter() {
    @Override
    public String getFormattedValue(float value, Entry entry, 
        int dataSetIndex, ViewPortHandler viewPortHandler) {
            return "";
        }
});
chartView.setData(new BarData(new String[]{"sleep"}, barDataSet));

并扩展HorizontalBarChartRenderer:

@Override
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
    // ...
    c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j + 2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
}

结果是:

如何只为外侧制作圆边?
像这样:

我建议您在 "normal" 模式下绘制除最后一个以外的所有条形图,并首先绘制顶部(圆角)条形图并让它横跨所有条形图的整个长度。

这样一来,其他未圆化的条形将掩盖第一个绘制(圆化)条形底部不需要的圆角。

我找到了解决办法。我的想法是像往常一样绘制数据,然后清除一些区域以使边缘变圆。 我的代码:

public class CircleHorizontalBarChartRenderer extends HorizontalBarChartRenderer {

    public CircleHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
        super(chart, animator, viewPortHandler);
    }

    @Override
    protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
        Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

        mShadowPaint.setColor(dataSet.getBarShadowColor());

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

        // initialize the buffer
        BarBuffer buffer = mBarBuffers[index];
        buffer.setPhases(phaseX, phaseY);
        buffer.setBarSpace(dataSet.getBarSpace());
        buffer.setDataSet(index);
        buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));

        buffer.feed(dataSet);

        trans.pointValuesToPixel(buffer.buffer);

        int length = buffer.buffer.length;
        float left = 0;
        float right = 0;
        float top = buffer.buffer[length - 3];
        float bot = buffer.buffer[length - 1];
        boolean leftSaved = false;

        for (int j = 0; j < buffer.size(); j += 4) {

            if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3]))
                break;

            if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
                continue;

            // Set the color for the currently drawn value. 
            // If the index is
            // out of bounds, reuse colors.
            int color = dataSet.getColor(j / 4);
            mRenderPaint.setColor(color);
            if (color != 0 && !leftSaved) {
                leftSaved = true;
                left = buffer.buffer[j];
            }
            if (j > 4) { // it works but its ugly
                right = buffer.buffer[j - 2];
            }

            c.drawRect(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j + 2],
                    buffer.buffer[j + 3] - 10, mRenderPaint);
        }

        Paint erasePaint = new Paint();
        erasePaint.setAntiAlias(true);
        erasePaint.setStyle(Paint.Style.STROKE);
        int paintWidth = 20;
        erasePaint.setStrokeWidth(paintWidth);
        erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        c.drawRoundRect(new RectF(left - paintWidth / 2, top, right + paintWidth / 2, bot), 30, 30, erasePaint);
    }

}

PorterDuff.Mode.CLEAR 仅当您将图层类型设置为软件时才有效。所以你需要调用

setLayerType(View.LAYER_TYPE_SOFTWARE, null);

用于图表视图或其父视图。如果您不需要透明背景,并且已知背景颜色,您可以为 erasePaint 设置此颜色,并且不使用软件层类型,这会降低性能。 erasePaint.setXfermode 这种情况也不需要。

最终结果:

我编辑了 Nick Kober 的解决方案。这是代码;

public class CircleHorizontalBarChartRenderer extends HorizontalBarChartRenderer {

    public CircleHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
        super(chart, animator, viewPortHandler);
    }

    @Override
    protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
        Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

        mShadowPaint.setColor(dataSet.getBarShadowColor());

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

        // initialize the buffer
        BarBuffer buffer = mBarBuffers[index];
        buffer.setPhases(phaseX, phaseY);
        buffer.setBarWidth(dataSet.getBarBorderWidth());
        buffer.setDataSet(index);
        buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));

        buffer.feed(dataSet);

        trans.pointValuesToPixel(buffer.buffer);

        int timeToChange = buffer.size() / 4;

        for (int j = 0; j < buffer.size(); j += 4) {
            if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3]))
                break;

            if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
                continue;

            // Set the color for the currently drawn value.
            // If the index is
            // out of bounds, reuse colors.
            int color = dataSet.getColor(j / 4);
            mRenderPaint.setColor(color);

            if (j/4 == 0) {
                c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
                c.drawRect(new RectF(buffer.buffer[j] + 10 , buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), mRenderPaint);
            }
            else if (j/4 < timeToChange - 1) {
                c.drawRect(new RectF(buffer.buffer[j] , buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), mRenderPaint);
            }
            else if (j/4 == timeToChange - 1) {
                c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
                c.drawRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2] - 10, buffer.buffer[j + 3] - 10), mRenderPaint);
            }
        }
    }

同样,您应该像 Nick 那样编写自己的自定义渲染器 class。我添加了最后一部分(if-else if 块)。

我是这样想的,为第一条画圆角矩形。然后在其上方通过将左侧移动到右侧来绘制一个矩形。通过移动,左侧保持圆形。对于内部的,将它们全部绘制为矩形。最后一个绘制圆角矩形。然后在上面通过将右侧移动到左侧来绘制一个矩形。通过移动,右侧保持圆润。

我使用变量“timeToChange”来了解我当前正在绘制哪个柱。

And the output is like this