Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MPAndroidChart - Round edged bar chart

I am trying to create a chart like below, For my android application using MPAndroidChart. I am unable to figure out, how to make the bar chart's edges as round edged. It always comes as square edge.

enter image description here

So can you please suggest me what I should do?

In advance thanks for your help.

like image 947
syed imty Avatar asked Jun 10 '15 15:06

syed imty


2 Answers

I try combined @mallaudin and @Janusz Hain code. It's working now, Thanks you!!! here's my code

public class CustomBarChartRender extends BarChartRenderer {

private RectF mBarShadowRectBuffer = new RectF();

private int mRadius;

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

public void setRadius(int mRadius) {
    this.mRadius = mRadius;
}

protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {

    Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
    mBarBorderPaint.setColor(dataSet.getBarBorderColor());
    mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth()));
    mShadowPaint.setColor(dataSet.getBarShadowColor());
    boolean drawBorder = dataSet.getBarBorderWidth() > 0f;

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

    if (mChart.isDrawBarShadowEnabled()) {
        mShadowPaint.setColor(dataSet.getBarShadowColor());

        BarData barData = mChart.getBarData();

        float barWidth = barData.getBarWidth();
        float barWidthHalf = barWidth / 2.0f;
        float x;

        int i = 0;
        double count = Math.min(Math.ceil((int) (double) ((float) dataSet.getEntryCount() * phaseX)), dataSet.getEntryCount());
        while (i < count) {

            BarEntry e = dataSet.getEntryForIndex(i);

            x = e.getX();

            mBarShadowRectBuffer.left = x - barWidthHalf;
            mBarShadowRectBuffer.right = x + barWidthHalf;

            trans.rectValueToPixel(mBarShadowRectBuffer);

            if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right)) {
                i++;
                continue;
            }

            if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left))
                break;

            mBarShadowRectBuffer.top = mViewPortHandler.contentTop();
            mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom();

            c.drawRoundRect(mBarRect, mRadius, mRadius, mShadowPaint);
            i++;
        }
    }

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

    buffer.feed(dataSet);

    trans.pointValuesToPixel(buffer.buffer);

    boolean isSingleColor = dataSet.getColors().size() == 1;

    if (isSingleColor) {
        mRenderPaint.setColor(dataSet.getColor());
    }

    int j = 0;
    while (j < buffer.size()) {

        if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) {
            j += 4;
            continue;
        }

        if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j]))
            break;

        if (!isSingleColor) {
            // Set the color for the currently drawn value. If the index
            // is out of bounds, reuse colors.
            mRenderPaint.setColor(dataSet.getColor(j / 4));
        }

        if (dataSet.getGradientColor() != null) {
            GradientColor gradientColor = dataSet.getGradientColor();
            mRenderPaint.setShader(new LinearGradient(
                    buffer.buffer[j],
                    buffer.buffer[j + 3],
                    buffer.buffer[j],
                    buffer.buffer[j + 1],
                    gradientColor.getStartColor(),
                    gradientColor.getEndColor(),
                    android.graphics.Shader.TileMode.MIRROR));
        }

        if (dataSet.getGradientColors() != null) {
            mRenderPaint.setShader(new LinearGradient(
                    buffer.buffer[j],
                    buffer.buffer[j + 3],
                    buffer.buffer[j],
                    buffer.buffer[j + 1],
                    dataSet.getGradientColor(j / 4).getStartColor(),
                    dataSet.getGradientColor(j / 4).getEndColor(),
                    Shader.TileMode.MIRROR));
        }
        Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                buffer.buffer[j + 3]), mRadius, mRadius, true, true, false, false);
        c.drawPath(path2, mRenderPaint);
        if (drawBorder) {
            Path path = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                    buffer.buffer[j + 3]), mRadius, mRadius, true, true, false, false);
            c.drawPath(path, mBarBorderPaint);
        }
        j += 4;
    }

}

private Path roundRect(RectF rect, float rx, float ry, boolean tl, boolean tr, boolean br, boolean bl) {
    float top = rect.top;
    float left = rect.left;
    float right = rect.right;
    float bottom = rect.bottom;
    Path path = new Path();
    if (rx < 0) rx = 0;
    if (ry < 0) ry = 0;
    float width = right - left;
    float height = bottom - top;
    if (rx > width / 2) rx = width / 2;
    if (ry > height / 2) ry = height / 2;
    float widthMinusCorners = (width - (2 * rx));
    float heightMinusCorners = (height - (2 * ry));

    path.moveTo(right, top + ry);
    if (tr)
        path.rQuadTo(0, -ry, -rx, -ry);//top-right corner
    else {
        path.rLineTo(0, -ry);
        path.rLineTo(-rx, 0);
    }
    path.rLineTo(-widthMinusCorners, 0);
    if (tl)
        path.rQuadTo(-rx, 0, -rx, ry); //top-left corner
    else {
        path.rLineTo(-rx, 0);
        path.rLineTo(0, ry);
    }
    path.rLineTo(0, heightMinusCorners);

    if (bl)
        path.rQuadTo(0, ry, rx, ry);//bottom-left corner
    else {
        path.rLineTo(0, ry);
        path.rLineTo(rx, 0);
    }

    path.rLineTo(widthMinusCorners, 0);
    if (br)
        path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner
    else {
        path.rLineTo(rx, 0);
        path.rLineTo(0, -ry);
    }

    path.rLineTo(0, -heightMinusCorners);

    path.close();//Given close, last lineto can be removed.

    return path;
}}

In other class:

CustomBarChartRender barChartRender = new CustomBarChartRender(barChart,barChart.getAnimator(), barChart.getViewPortHandler());
    barChartRender.setRadius(20);
    barChart.setRenderer(barChartRender);
like image 60
duc tan Avatar answered Oct 18 '22 17:10

duc tan


I've implemented a solution to achieve that straight on the library itself.

Firstly create an attrs.xml adding a new attribute to use on your chart view. Something like this:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="BarChart">
        <attr name="radius" format="integer" />
    </declare-styleable>
</resources>

Then edit the method called drawDataSet on the BarChartRenderer:

protected void drawDataSet(Canvas c, BarDataSet dataSet, int index) {

    Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

    mShadowPaint.setColor(dataSet.getBarShadowColor());

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

    List<BarEntry> entries = dataSet.getYVals();

    // 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(entries);

    trans.pointValuesToPixel(buffer.buffer);

    // if multiple colors
    if (dataSet.getColors().size() > 1) {

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

            if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2]))
                continue;

            if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j]))
                break;

            if (mChart.isDrawBarShadowEnabled()) {
                if (mRadius > 0)
                    c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(),
                            buffer.buffer[j + 2],
                            mViewPortHandler.contentBottom()), mRadius, mRadius, mShadowPaint);
                else
                    c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(),
                            buffer.buffer[j + 2],
                            mViewPortHandler.contentBottom(), mShadowPaint);
            }

            // Set the color for the currently drawn value. If the index
            // is
            // out of bounds, reuse colors.
            mRenderPaint.setColor(dataSet.getColor(j / 4));
            if (mRadius > 0)
                c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                        buffer.buffer[j + 3]), mRadius, mRadius, mRenderPaint);
            else
                c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                        buffer.buffer[j + 3], mRenderPaint);
        }
    } else {

        mRenderPaint.setColor(dataSet.getColor());

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

            if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2]))
                continue;

            if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j]))
                break;

            if (mChart.isDrawBarShadowEnabled()) {
                if (mRadius > 0)
                    c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(),
                            buffer.buffer[j + 2],
                            mViewPortHandler.contentBottom()), mRadius, mRadius, mShadowPaint);
                else
                    c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                            buffer.buffer[j + 3], mRenderPaint);
            }

            if (mRadius > 0)
                c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                        buffer.buffer[j + 3]), mRadius, mRadius, mRenderPaint);
            else
                c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                        buffer.buffer[j + 3], mRenderPaint);
        }
    }
}

By doing this you're changing the rectangle but not its highlight, so change this bit of code on the drawHighlighted method:

if (mRadius > 0)
      c.drawRoundRect(mBarRect, mRadius, mRadius, mHighlightPaint);
else
      c.drawRect(mBarRect, mHighlightPaint);

To get the attribute from the xml file onto this render you need to add a set method as well:

public void setRadius (int radius) {
        mRadius = radius;
}

Finally create a new constructor on the BarChart object to grab the radius attribute:

public BarChart(Context context, AttributeSet attrs) {
        super(context, attrs);
        mRadius = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto", "radius", 0);
        ((BarChartRenderer)mRenderer).setRadius(mRadius);
    }

And voila! Happy coding :)

like image 25
RicardoSousaDev Avatar answered Oct 18 '22 18:10

RicardoSousaDev