Skip to content

Instantly share code, notes, and snippets.

@xanscale
Last active January 8, 2024 22:19
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save xanscale/e971cc4f2f0712a8a3bcc35e85325c27 to your computer and use it in GitHub Desktop.
Save xanscale/e971cc4f2f0712a8a3bcc35e85325c27 to your computer and use it in GitHub Desktop.
MPAndroidChart BarChart with rounded corner (based from v3.1.0). Just replaced drawRect with drawRoundRect
<com.scarozza.RoundedBarChart
android:id="@+id/chart"
app:radius="4dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundedBarChart">
<attr name="radius" format="dimension" />
</declare-styleable>
</resources>
package com.scarozza;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.RectF;
import android.util.AttributeSet;
import com.github.mikephil.charting.animation.ChartAnimator;
import com.github.mikephil.charting.buffer.BarBuffer;
import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.highlight.Range;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.model.GradientColor;
import com.github.mikephil.charting.renderer.BarChartRenderer;
import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.Utils;
import com.github.mikephil.charting.utils.ViewPortHandler;
public class RoundedBarChart extends BarChart {
public RoundedBarChart(Context context) {
super(context);
}
public RoundedBarChart(Context context, AttributeSet attrs) {
super(context, attrs);
readRadiusAttr(context, attrs);
}
public RoundedBarChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
readRadiusAttr(context, attrs);
}
private void readRadiusAttr(Context context, AttributeSet attrs){
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RoundedBarChart, 0, 0);
try {
setRadius(a.getDimensionPixelSize(R.styleable.RoundedBarChart_radius, 0));
} finally {
a.recycle();
}
}
public void setRadius(int radius) {
setRenderer(new RoundedBarChartRenderer(this, getAnimator(), getViewPortHandler(), radius));
}
private class RoundedBarChartRenderer extends BarChartRenderer {
private int mRadius;
private RectF mBarShadowRectBuffer = new RectF();
RoundedBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, int mRadius) {
super(chart, animator, viewPortHandler);
this.mRadius = mRadius;
}
@Override
public void drawHighlighted(Canvas c, Highlight[] indices) {
BarData barData = mChart.getBarData();
for (Highlight high : indices) {
IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex());
if (set == null || !set.isHighlightEnabled())
continue;
BarEntry e = set.getEntryForXValue(high.getX(), high.getY());
if (!isInBoundsX(e, set))
continue;
Transformer trans = mChart.getTransformer(set.getAxisDependency());
mHighlightPaint.setColor(set.getHighLightColor());
mHighlightPaint.setAlpha(set.getHighLightAlpha());
boolean isStack = high.getStackIndex() >= 0 && e.isStacked();
final float y1;
final float y2;
if (isStack) {
if (mChart.isHighlightFullBarEnabled()) {
y1 = e.getPositiveSum();
y2 = -e.getNegativeSum();
} else {
Range range = e.getRanges()[high.getStackIndex()];
y1 = range.from;
y2 = range.to;
}
} else {
y1 = e.getY();
y2 = 0.f;
}
prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans);
setHighlightDrawPos(high, mBarRect);
c.drawRoundRect(mBarRect, mRadius, mRadius, mHighlightPaint);
}
}
@Override
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()));
final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f;
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
// draw the bar shadow before the values
if (mChart.isDrawBarShadowEnabled()) {
mShadowPaint.setColor(dataSet.getBarShadowColor());
BarData barData = mChart.getBarData();
final float barWidth = barData.getBarWidth();
final float barWidthHalf = barWidth / 2.0f;
float x;
for (int i = 0, count = Math.min((int) (Math.ceil((float) (dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount());
i < count;
i++) {
BarEntry e = dataSet.getEntryForIndex(i);
x = e.getX();
mBarShadowRectBuffer.left = x - barWidthHalf;
mBarShadowRectBuffer.right = x + barWidthHalf;
trans.rectValueToPixel(mBarShadowRectBuffer);
if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right))
continue;
if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left))
break;
mBarShadowRectBuffer.top = mViewPortHandler.contentTop();
mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom();
c.drawRoundRect(mBarShadowRectBuffer, mRadius, mRadius, mShadowPaint);
}
}
// 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);
final boolean isSingleColor = dataSet.getColors().size() == 1;
if (isSingleColor) {
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 (!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(),
android.graphics.Shader.TileMode.MIRROR));
}
c.drawRoundRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3], mRadius, mRadius, mRenderPaint);
if (drawBorder) {
c.drawRoundRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3], mRadius, mRadius, mBarBorderPaint);
}
}
}
}
}
@dec04
Copy link

dec04 commented Mar 6, 2020

great work, thanks

@berk-can
Copy link

Thank you so much can you please create MR to MPAndroidChart this should come in library itself IMO

@xanscale
Copy link
Author

PhilJay/MPAndroidChart#2771 (comment)

yes, but the project seams abandoned

@iamsr
Copy link

iamsr commented Feb 27, 2021

how about rounding from both ends?

@sum20156
Copy link

how about rounding from both ends?

(Kotlin)
use chart.axisLeft.axisMinimum = 0f to make corner rounded from both end

@samueldanda
Copy link

Finally a working solution after almost 2 month of hustling to find the solution

@Monabr
Copy link

Monabr commented Mar 13, 2022

Cannot resolve symbol 'radius'

@anuj-2255
Copy link

great work thanks for the solution

@mahditavakoli1312
Copy link

greate work ,
how about HorizontalRoundedBarChart ?

@mahditavakoli1312
Copy link

greate work , how about HorizontalRoundedBarChart ?

solved , look https://github.com/mahditavakoli1312/mpchartandroid.git

@efeint01
Copy link

efeint01 commented Apr 5, 2023

u saved my time thankss

@arifyudhistirapcs
Copy link

how to give only both top radius?

@svksricharan
Copy link

Hi you can add this code in this overridden method

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

float barLeft = buffer.buffer[j];
float barTop = buffer.buffer[j + 1];
float barRight = buffer.buffer[j + 2];
float barBottom = buffer.buffer[j + 3];

            // Adjust rounded corners for top left and top right corners
            float[] radii = new float[]{
                    mRadius, mRadius, mRadius, mRadius,
                    0f, 0f, 0f, 0f
            };

            // Draw the rounded rectangle with adjusted corners
            Path path = new Path();
            path.addRoundRect(new RectF(barLeft, barTop, barRight, barBottom), radii, Path.Direction.CW);

            c.drawPath(path, mRenderPaint);

            if (drawBorder) {
                c.drawPath(path, mBarBorderPaint);
            }

// comment this code
/*c.drawRoundRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3], mRadius, mRadius, mRenderPaint);

            if (drawBorder) {
                c.drawRoundRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                        buffer.buffer[j + 3], mRadius, mRadius, mBarBorderPaint);
            }*/

@Rafdadoumont
Copy link

Amazing, great work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment