Home » Android » android – MPAndroidChart – Round edged bar chart

android – MPAndroidChart – Round edged bar chart

Posted by: admin May 14, 2020 Leave a comment

Questions:

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.

How to&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);

Answer:

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 🙂

Answer:

created a complete gist from last code of MPAndroidChart

https://gist.github.com/xanscale/e971cc4f2f0712a8a3bcc35e85325c27

Answer:

For this purpose you need to custimze your BarchartRenderer class….

Step 1

Make a custom class (if you are not adding mpchart as module ) Copy paste all the code from BarchartRenderer class into ur custom class.
Now replace your drawDataSet method with mine in ur custom class…..

Step 2

After that setRender to the custom class you just made.

Kotlin Code for setting render Java code will be kinda same.

In the end enjoy

BarChartCustomRenderer customRenderer = BarChartCustomRenderer(mDashBoardBinding.topperChart, mDashBoardBinding.topperChart.getAnimator(), mDashBoardBinding.topperChart.getViewPortHandler());

topperChart.setRenderer(customRenderer);

drawDataSet:

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.setDataSet(index);
            buffer.setBarWidth(mChart.getBarData().getBarWidth());
            buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));

            buffer.feed(dataSet);

            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);
                }
            }
        }

Answer:

The above answer is right but it doesn’t work if graph has negative values.

I have picked the rounded rectangle code form this answer.

Steps

  1. Add the following method to BarChartRenderer class

    /**
     * @param rect rectangle to be rounded
     * @param rx   radius x
    * @param ry   radius y
    * @param tl   true - for rounding top-left corner
    * @param tr   true - for rounding top-right corner
    * @param br   true - for rounding bottom-right corner
    * @param bl   true - for rounding bottom-left corner
    * @return path
    */
    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;
    }
    
  2. In drawDataSet(Canvas c, IBarDataSet dataSet, int index) method, replace all drawRoundRect methods with the following code

     Path path = roundRect(yourRect, yourRadius, yourRadius, true, true, false, false);
                    canvas.drawPath(path, yourPaint);
    

Answer:

I have created class based on above for actual library (which has different code for drawing) – v3.1.0-alpha

class RoundedBarChartRenderer(chart: BarDataProvider,
                          animator: ChartAnimator,
                          viewPortHandler: ViewPortHandler,
                          private val mRadius: Float) :
    BarChartRenderer(chart, animator, viewPortHandler) {

private val mBarShadowRectBuffer = RectF()

override fun drawDataSet(c: Canvas?, dataSet: IBarDataSet?, index: Int) {

    if (c == null || dataSet == null) return

    val trans = mChart.getTransformer(dataSet.axisDependency)

    mBarBorderPaint.color = dataSet.barBorderColor
    mBarBorderPaint.strokeWidth = Utils.convertDpToPixel(dataSet.barBorderWidth)

    val drawBorder = dataSet.barBorderWidth > 0f

    val phaseX = mAnimator.phaseX
    val phaseY = mAnimator.phaseY

    // draw the bar shadow before the values
    if (mChart.isDrawBarShadowEnabled) {
        mShadowPaint.color = dataSet.barShadowColor

        val barData = mChart.barData

        val barWidth = barData.barWidth
        val barWidthHalf = barWidth / 2.0f
        var x: Float

        var i = 0
        val count = Math.min(Math.ceil((dataSet.entryCount.toFloat() * phaseX).toDouble()).toInt(), dataSet.entryCount)
        while (i < count) {

            val e = dataSet.getEntryForIndex(i)

            x = e.x

            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
    val buffer = mBarBuffers[index]
    buffer.setPhases(phaseX, phaseY)
    buffer.setDataSet(index)
    buffer.setInverted(mChart.isInverted(dataSet.axisDependency))
    buffer.setBarWidth(mChart.barData.barWidth)

    buffer.feed(dataSet)

    trans.pointValuesToPixel(buffer.buffer)

    val isSingleColor = dataSet.colors.size == 1

    if (isSingleColor) {
        mRenderPaint.color = dataSet.color
    }

    var 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.color = dataSet.getColor(j / 4)
        }

        if (dataSet.gradientColor != null) {
            val gradientColor = dataSet.gradientColor
            mRenderPaint.shader = LinearGradient(
                    buffer.buffer[j],
                    buffer.buffer[j + 3],
                    buffer.buffer[j],
                    buffer.buffer[j + 1],
                    gradientColor.startColor,
                    gradientColor.endColor,
                    android.graphics.Shader.TileMode.MIRROR)
        }

        if (dataSet.gradientColors != null) {
            mRenderPaint.shader = LinearGradient(
                    buffer.buffer[j],
                    buffer.buffer[j + 3],
                    buffer.buffer[j],
                    buffer.buffer[j + 1],
                    dataSet.getGradientColor(j / 4).startColor,
                    dataSet.getGradientColor(j / 4).endColor,
                    Shader.TileMode.MIRROR)
        }


        c.drawRoundRect(RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                buffer.buffer[j + 3]), mRadius, mRadius, mRenderPaint)

        if (drawBorder) {
            c.drawRoundRect(RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                    buffer.buffer[j + 3]), mRadius, mRadius, mBarBorderPaint)
        }
        j += 4
    }
}

}