Hot questions for Bar Chart in MPAndroidChart

Displaying a BarChart using mpandroidchart with kotlin

Question: Am trying to use Kotlin to display a simple bar chart using mpandroidchart library. Most of the examples I've been finding online are using java, so copy the code, paste in android studio to convert to Kotlin, then try to fix the errors were necessary. But i just can't seem to fix the one appearing when using the BarEntry as shown in the image. Any help is appreciated.

Answer: BarEntry only accept Float and FloatArray. You are passing Int instead of Float. Use f after each value like:

val barEntry = BarEntry(8f, 0f)

Or convert your value to Float using toFloat() provided by Kotlin like:

val barEntry = BarEntry(8f, 0.toFloat())

Switching between HorizontalScrollView and BarChart scroll

Question: I have a MPAndroidChart BarChart which is placed inside a HorizontalScrollView. Now, I have multiple bars in the chart view and also the chart width is fixed as per my requirement. Since, there is a default scroll behaviour of chart, I can see all the bars.

The only problem is the scroll behaviour is not smooth and it lags a lot.

My idea is to disable the HorizontalScrollView once I start scrolling the chart and enable it back when I touch outside the chart. Can someone please tell me how I can do it? I hope my question is clear enough and there is no need to share any XML for it. Thanks in advance.

Answer: I faced similar issue while using Mpchart inside horizontal scroll view. I had a workaround with MpChart setOnChartGestureListener.

barChart.setOnChartGestureListener(new OnChartGestureListener() {
        @Override
        public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
            horizontalScrollView.requestDisallowInterceptTouchEvent(true);
        }

        @Override
        public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
            horizontalScrollView.requestDisallowInterceptTouchEvent(false);
        }

        //.....//

        @Override
        public void onChartTranslate(MotionEvent me, float dX, float dY) {
            Log.i("GESTURE", "onChartTranslate");
            if(barChart.getLowestVisibleX() == barChart.getXAxis().getAxisMinimum() || barChart.getHighestVisibleX() == barChart.getXAxis().getAxisMaximum()) {
                horizontalScrollView.requestDisallowInterceptTouchEvent(false);
            } else {
                horizontalScrollView.requestDisallowInterceptTouchEvent(true);
            }
        }
    });

the horizontalScrollView object is where the chart reside.


MPAndroidChart Weekly data

Question: So I am trying to do a weekly chart with current weeks data vs previous weeks data using MPAndroidChart. I can do single bars just fine and everything lines up, but when I try to do multi bar groups it does the following.

Is there something else I need to be doing? I only have 2 bars per group for each day. Here is my current code:

BarData barDataPayments = new BarData(barDataSetCurrentWeek, barDataSetPrevWeek);
        barChartPayments.setData(barDataPayments);

        XAxis xAxisPayments = barChartPayments.getXAxis();
        xAxisPayments.setValueFormatter(new IndexAxisValueFormatter(labelWeekdayNames));

        xAxisPayments.setYOffset(5);
        xAxisPayments.setPosition(XAxis.XAxisPosition.BOTTOM);
        xAxisPayments.setLabelCount(labelWeekdayNames.size());
        xAxisPayments.setGranularity(1f);
        xAxisPayments.setCenterAxisLabels(false);
        xAxisPayments.setDrawGridLines(false);

        float groupSpace = 0.08f;
        float barSpace = 0.03f;
        float barWidth = 0.2f;

        barDataPayments.setBarWidth(barWidth);
        barChartPayments.getXAxis().setAxisMinimum(0);
        barChartPayments.getXAxis().setAxisMaximum(0 + barChartPayments.getBarData().getGroupWidth(groupSpace, barSpace) * 14);
        barChartPayments.groupBars(0, groupSpace, barSpace);

        barChartPayments.invalidate();

Answer: So what I ended up having to do is manually set the data. It took a little tweaking to get it just how I wanted it, I am sure there is a more proper way, but so far this was the easiest way that I have currently found.

xAxisMiles.setYOffset(0); // <---- CHANGED TO ZERO
xAxisMiles.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxisMiles.setLabelCount(labelWeekdayNames.size());
xAxisMiles.setGranularity(1f);
xAxisMiles.setCenterAxisLabels(true); // <---- SET TO TRUE
xAxisMiles.setDrawGridLines(false);

//IMPORTANT *****
barDataPayments.setBarWidth(0.40f);
barDataPayments.getXAxis().setAxisMinimum(0);
barDataPayments.getXAxis().setAxisMaximum(7);
barDataPayments.groupBars(0, 0.10f, 0.05f);
//***** IMPORTANT*/

Align MpAndroidChart Bar graph on bottom without XAxis

Question: I am using bar graph from MpAndroidCahrt on android app.

The problem is bars on graph doesn't align on the bottom.

It has a slight padding between bars and bottom side.

I'm not using XAxis and YAxis either.

Is there are some solutions?

Below is my code.

chart.data = barData
    chart.barData.barWidth = 0.5f
    chart.setFitBars(true)
    chart.setDrawGridBackground(false)
    chart.setPinchZoom(false)
    chart.axisRight.isEnabled = false
    chart.xAxis.isEnabled = false
    chart.axisLeft.removeAllLimitLines()
    chart.axisLeft.addLimitLine(limitLine)
    chart.axisLeft.isEnabled = false
    chart.isDoubleTapToZoomEnabled = false
    chart.description.isEnabled = false
    chart.legend.isEnabled = false
    chart.invalidate()

I added screenshot for more information.

Answer: Padding can be removed by using

chart.setViewPortOffsets(0f, 0f, 0f, 0f);

How to create horizontal Scrollable Grouped BarChart in andorid

Question: I need to crate this type of horizontal Scrollable Grouped BarChart

I don't know how create exact same Grouped BarChart as above image and make it horizontal Scrollable

Can anybody help me create this type of horizontal Scrollable Grouped BarChart

Answer: Please try the following sample code:

class ChartFragment : BaseFragment() {

    lateinit var mChart: BarChart
    lateinit var rootView: View

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        rootView = inflater.inflate(R.layout.fragment_insigts, container, false)

        rootView.tvInflow.text = changeTextSizeOfAvailableBalance("198.12")
        rootView.tvOutFlow.text = changeTextSizeOfAvailableBalance("198.12")

        mChart = rootView.barChart
        getEntries()

        return rootView
    }

    private fun changeTextSizeOfAvailableBalance(price: String): SpannableStringBuilder {
        val spannableStringBuilder = SpannableStringBuilder("N")
        val spannable = SpannableString(price)
        spannable.setSpan(RelativeSizeSpan(2f), 0, spannable.length, 0)
        spannableStringBuilder.append(spannable)
        return spannableStringBuilder
    }

    private fun getEntries() {
        mChart.setDrawBarShadow(false)
        mChart.description.isEnabled = false
        mChart.setPinchZoom(false)
        mChart.setDrawGridBackground(false)

        val labels = arrayOf("", "JAN", "FEB", "MAR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", "")

        val xAxis = mChart.xAxis
        xAxis.setCenterAxisLabels(true)
        xAxis.position = XAxis.XAxisPosition.BOTTOM
        xAxis.setDrawGridLines(false)
        xAxis.granularity = 1f // only intervals of 1 day
        xAxis.textColor = Color.BLACK
        xAxis.textSize = 11f
        xAxis.axisLineColor = Color.WHITE
        xAxis.axisMinimum = 1f
        xAxis.valueFormatter = IndexAxisValueFormatter(labels)
        xAxis.position = XAxis.XAxisPosition.TOP

        val leftAxis = mChart.axisLeft
        leftAxis.textColor = Color.BLACK
        leftAxis.textSize = 12f
        leftAxis.axisLineColor = Color.WHITE
        leftAxis.setDrawGridLines(false)
        leftAxis.granularity = 2f
        leftAxis.setLabelCount(8, true)
        leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)

        mChart.axisRight.isEnabled = false
        mChart.legend.isEnabled = false

        val valOne = floatArrayOf(10f, 20f, 44f, 40f, 50f, 50f, 52f, 30f, 40f, 50f, 40f, 30f)
        val valTwo = floatArrayOf(20f, 10f, 50f, 30f, 52f, 52f, 50f, 40f, 30f, 40f, 30f, 30f)

        val barOne = ArrayList<BarEntry>()
        val barTwo = ArrayList<BarEntry>()

        for (i in 0 until valOne.size) {
            barOne.add(BarEntry(i.toFloat(), valOne[i]))
            barTwo.add(BarEntry(i.toFloat(), valTwo[i]))
        }

        val set1 = BarDataSet(barOne, "barOne")
        set1.color = Color.GRAY
        val set2 = BarDataSet(barTwo, "barTwo")
        set2.color = Color.BLUE


        set1.isHighlightEnabled = false
        set2.isHighlightEnabled = false
        set1.setDrawValues(false)
        set2.setDrawValues(false)

        mChart.axisLeft.setDrawLabels(false)
        mChart.axisRight.setDrawLabels(false)

        val dataSets = ArrayList<IBarDataSet>()
        dataSets.add(set1)
        dataSets.add(set2)
        val data = BarData(dataSets)
        val groupSpace = 0.4f
        val barSpace = 0f
        val barWidth = 0.3f
        data.barWidth = barWidth
        xAxis.axisMaximum = labels.size - 1.1f
        mChart.data = data
        mChart.setScaleEnabled(false)
        mChart.setVisibleXRangeMaximum(4f)
        mChart.groupBars(1f, groupSpace, barSpace)
        mChart.invalidate()
    }
}

output


Rotate value text vertically outside bar in BarChart MPAndroidChart

Question: Rotate value text vertically outside bar in arChart MPAndroidChart

I am able to rotate the text but it is coming inside the bar which I have achieved using code below:

chart.setRenderer(CustomBarChartRenderer(context, this, this.getAnimator(), this.getViewPortHandler()))
public class CustomBarChartRenderer extends BarChartRenderer {
      public CustomBarChartRenderer(BarDataProvider chart, 
                                    ChartAnimator animator, ViewPortHandler viewPortHandler) {
           super(chart, animator, viewPortHandler);
      }

      @Override
      public void drawValue(Canvas canvas, IValueFormatter formatter, 
                            float value, Entry entry, int dataSetIndex, float x, float y, int color) {
           mValuePaint.setColor(color);

           canvas.save();
           canvas.rotate(-90);

           canvas.drawText(formatter.getFormattedValue(value, entry, 
                                                       dataSetIndex, mViewPortHandler), x, y, mValuePaint);
           canvas.restore();
      }
}

Text is rotated but comes inside the bar can see in the image link below:

How to make text value properly align above the bar?

Answer: I was able to fix it by moving the label up.

Worth noticing is x and y dimensions changes internally as we are rotating the label so for moving label up you have to increase x value and not y value.

Also note to make it dynamic and work for all text lengths, I am calculating the pixel size for text to be displayed and moving label up by that pixel size only. See code below (in kotlin):

override fun drawValue(c: Canvas, formatter: IValueFormatter, value: Float, entry: Entry, dataSetIndex: Int, x: Float, y: Float, color: Int) {
    mValuePaint.color = Color.WHITE
    val text = formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler)

    c.save()
    val width = mValuePaint.measureText(text)
    c.rotate(-90f, x, y)

    c.drawText(text, x + (width / 2), y, mValuePaint)
    c.restore()
}

Show only some values in MPAndroidChart

Question: I have an array of y values that I am displaying over the dates of a month. To simplify, for the first week of April, I would have the values {0,200,0,0,500,0,100} over the x values {1,2,3,4,5,6,7}. I am able to display them as a bar chart using MPAndroidChart. I am also able to hide and display the values over each bar using

barChart.getData().setDrawValues(true); //or false when I want to hide 

However, I want to display only the number that are non-zero, how would I be able to do so? Any pointers would be appreciated!

I tried creating my formatter the following way:

public class MyYAxisValueFormatter implements IAxisValueFormatter {

        private DecimalFormat mFormat;

        public MyYAxisValueFormatter() {
        // format values to 1 decimal digit
            mFormat = new DecimalFormat("###,###,##0.0");
        }

        @Override
        public String getFormattedValue(float value, AxisBase axis) {
            String val = "";

            if(value != 0)
                val = String.valueOf(value);

            return mFormat.format(value) + " $";
        }
    }

and called it using this in my main function:

YAxis yAxis = barChart.getAxisLeft();
yAxis.setValueFormatter(new MyYAxisValueFormatter());

However the values of zero are still displayed.

Answer: Try making your own IValueFormatter Interface

public class MyYAxisValueFormatter implements IValueFormatter {

        private DecimalFormat mFormat;

        public MyYAxisValueFormatter() {
        // format values to 1 decimal digit
            mFormat = new DecimalFormat("###,###,##0.0");
        }

        @Override
        public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
            // "value" represents the position of the label on the axis (x or y)
             if(value > 0) {
               return mFormat.format(value);
             } else {
               return "";
             }
        }

try setting value formatter to your barchart.

bar.setValueFormatter(new MyYAxisValueFormatter ());

How to put customized value&image per bar in MPAndroidChart?

Question: I'm using MPAndroidChart to show a relatively simple bar chart.

There are 2 things I need to set, that I can't figure out how to customize:

1. Instead of simple values, I need to add text per each bar, which by itself is also styled.

2. On top of each bar, I need to put various types of drawable that cover it in width (for example blue with height of 2dp in one bar, or yellow gradient with same height on another bar).

Here's a demonstration of what I need to do:

As I wrote, I'd like to know if the above are possible, and how:

1. How can I set a customized, styled value above each bar ?

2. How can I set a different drawable to "sit" on top of each bar?

3. If #2 is not possible (and maybe even if it is), is it possible to set a drawable to be the bar itself? For example, some bars would be a solid gray color, and some bars would have a gradient yellow drawable?

Answer: You need custom bar BarChartRenderer to achieve this. I have provided a rough sample.

Code for setting the barchart

    public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // custom colors that you want on top of the bars
        ArrayList<Integer> myColors = new ArrayList<>();
        myColors.add(Color.BLACK);
        myColors.add(Color.YELLOW);
        myColors.add(Color.BLUE);
        myColors.add(Color.DKGRAY);
        myColors.add(Color.GREEN);
        myColors.add(Color.GRAY);

        String[] myText = {"A Round", "B Round", "C Round", "D Round", "E Round", "F Round"};


        BarChart mChart = (BarChart) findViewById(R.id.barChart);
        mChart.setDrawBarShadow(false);
        mChart.getDescription().setEnabled(false);
        mChart.setDrawGridBackground(false);

        XAxis xaxis = mChart.getXAxis();
        xaxis.setDrawGridLines(false);
        xaxis.setPosition(XAxis.XAxisPosition.BOTTOM);

        xaxis.setDrawLabels(true);
        xaxis.setDrawAxisLine(false);

        YAxis yAxisLeft = mChart.getAxisLeft();
        yAxisLeft.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);
        yAxisLeft.setDrawGridLines(false);
        yAxisLeft.setDrawAxisLine(false);
        yAxisLeft.setEnabled(false);

        mChart.getAxisRight().setEnabled(false);
        // set your custom renderer
        mChart.setRenderer(new BarChartCustomRenderer(mChart, mChart.getAnimator(), mChart.getViewPortHandler(), myColors));
        mChart.setDrawValueAboveBar(true);

        Legend legend = mChart.getLegend();
        legend.setEnabled(false);

        ArrayList<BarEntry> valueSet1 = new ArrayList<BarEntry>();

        for (int i = 0; i < 6; ++i) {
            BarEntry entry = new BarEntry(i, (i + 1) * 10);
            valueSet1.add(entry);
        }


        List<IBarDataSet> dataSets = new ArrayList<>();
        BarDataSet barDataSet = new BarDataSet(valueSet1, " ");
        barDataSet.setValueFormatter(new MyFormatter(myText));
        barDataSet.setColor(Color.CYAN);
        dataSets.add(barDataSet);

        BarData data = new BarData(dataSets);
        mChart.setData(data);
    }


    public class MyFormatter implements IValueFormatter {

        String[] text;

        public MyFormatter(String[] text) {
            this.text = text;
        }

        @Override
        public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
            return String.valueOf((int)value)+"M" + ", " + text[(int) entry.getX()];
        }
    }
}

Custom Renderer

    public class BarChartCustomRenderer extends BarChartRenderer {

    private Paint myPaint;
    private ArrayList<Integer> myColors;

    public BarChartCustomRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, ArrayList<Integer> myColors) {
        super(chart, animator, viewPortHandler);
        this.myPaint = new Paint();
        this.myColors = myColors;
    }

    @Override
    public void drawValues(Canvas c) {
        super.drawValues(c);
        // you can modify the original method
        // so that everything is drawn on the canvas inside a single loop
        // also you can add logic here to meet your requirements
        int colorIndex = 0;
        for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) {
            BarBuffer buffer = mBarBuffers[i];
            float left, right, top, bottom;
            for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) {
                myPaint.setColor(myColors.get(colorIndex++));
                left = buffer.buffer[j];
                right = buffer.buffer[j + 2];
                top = buffer.buffer[j + 1];
                bottom = buffer.buffer[j + 3];
//                myPaint.setShader(new LinearGradient(left,top,right,bottom, Color.CYAN, myColors.get(colorIndex++), Shader.TileMode.MIRROR ));
                c.drawRect(left, top, right, top+5f, myPaint);
            }
        }
    }

    @Override
    public void drawValue(Canvas c, IValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) {
        String text = formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler);
        String[] splitText;
        if(text.contains(",")){
            splitText = text.split(",");
            Paint paintStyleOne = new Paint(mValuePaint);
            Paint paintStyleTwo = new Paint(mValuePaint);
            paintStyleOne.setColor(Color.BLACK);
            paintStyleTwo.setColor(Color.BLUE);
            c.drawText(splitText[0], x, y-20f, paintStyleOne);
            c.drawText(splitText[1], x, y, paintStyleTwo);
        }
        //else{
//            super.drawValue(c, formatter, value, entry, dataSetIndex, x, y, color);
        //}
    }
}

RESULT

you can also do a gradient effect for the entire bar by slightly modifying the custom renderer :

myPaint.setShader(new LinearGradient(left,top,right,bottom, Color.CYAN, myColors.get(colorIndex++), Shader.TileMode.MIRROR ));
c.drawRect(left, top, right, bottom, myPaint);

you can similarly draw and style your text using the custom renderer.

Update for using drawables instead of colors

//get bitmap from a drawable
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.myDrawable);

after than you can create a list of bitmaps and pass in onto the renderer instead of the list of colors.

if you want to draw on just the top of the bar you can use this :

c.drawBitmap(bitmap.get(index++), null, new RectF(left, top, right, top+5f), null);

or if you want to cover the entire bar, you can do so by using the bitmap like this:

c.drawBitmap(bitmap.get(index++), null, new RectF(left, top, right, bottom), null);

How to set two decimal point into Bar Entry in Mp android Bar Chart in Android

Question: How to set JSON data in bar entry in two decimal point?

Answer: You can use The ValueFormatter interface

The IValueFormatter interface can be used to create custom-made formatter classes that allow to format values within the chart (from DataSets) in a specific way before drawing them.

For using the IValueFormatter, simply create a new class and let it implement the interface and return whatever you want to be displayed from the getFormattedValue(...) method.

Creating a Formatter

public class MyValueFormatter implements IValueFormatter {

    private DecimalFormat mFormat;

    public MyValueFormatter() {
        mFormat = new DecimalFormat("###,###,##0.0"); // use one decimal
    }

    @Override
    public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
        // write your logic here
        return mFormat.format(value) + " $"; // e.g. append a dollar-sign
    }
}

Then, set your formatter to the ChartData or DataSet object:

// usage on whole data object
lineData.setValueFormatter(new MyValueFormatter());

// usage on individual dataset object
lineDataSet.setValueFormatter(new MyValueFormatter());

How do I move bar to center in chart in MPAndroidChart?

Question: I'm using the MPAndroidChart.

It's nice library but I couldn't have solved a problem.

This is result be executed.

The axis are well to center align but bar groups are no center align.

I would like to move bar groups to center too.

And, This is entire code.

float barWidth = 0.1f;
        float barSpace = 0f;
        float groupSpace = 0.1f;
        int groupCount = 3;

        distinctionGraph = (BarChart) findViewById(R.id.distinction_graph);
        verificationGraph = (BarChart) findViewById(R.id.verification_graph);
        sentenceGraph = (BarChart) findViewById(R.id.sentence_graph);

        ArrayList xVals = new ArrayList();

        xVals.add("3월");
        xVals.add("2월");
        xVals.add("1월");

        ArrayList yVals1 = new ArrayList();
        ArrayList yVals2 = new ArrayList();
        ArrayList yVals3 = new ArrayList();

        yVals1.add(new BarEntry(1, (float) 1));
        yVals2.add(new BarEntry(1, (float) 2));
        yVals3.add(new BarEntry(1, (float) 2));

        yVals1.add(new BarEntry(2, (float) 3));
        yVals2.add(new BarEntry(2, (float) 4));
        yVals3.add(new BarEntry(2, (float) 4));

        yVals1.add(new BarEntry(3, (float) 5));
        yVals2.add(new BarEntry(3, (float) 6));
        yVals3.add(new BarEntry(3, (float) 6));

        BarDataSet set1, set2, set3;
        set1 = new BarDataSet(yVals1, "쉬움");
        set1.setColor(Color.parseColor("#3EBB9B"));
        set2 = new BarDataSet(yVals2, "보통");
        set2.setColor(Color.parseColor("#3698DB"));
        set3 = new BarDataSet(yVals3, "어려움");
        set3.setColor(Color.parseColor("#2C80B9"));

        BarData data = new BarData(set1, set2, set3);
        data.setValueFormatter(new LargeValueFormatter());

        distinctionGraph.setData(data);
        distinctionGraph.getBarData().setBarWidth(barWidth);
        distinctionGraph.getXAxis().setAxisMinimum(0);
        distinctionGraph.getXAxis().setAxisMaximum(0 + distinctionGraph.getBarData().getGroupWidth(groupSpace, barSpace) * groupCount);
        distinctionGraph.groupBars(0, groupSpace, barSpace);
        distinctionGraph.getData().setHighlightEnabled(false);
        distinctionGraph.invalidate();

        distinctionGraph.setDescription(null);
        distinctionGraph.setPinchZoom(false);
        distinctionGraph.setScaleEnabled(false);
        distinctionGraph.setDrawBarShadow(false);
        distinctionGraph.setDrawGridBackground(false);

        Legend l = distinctionGraph.getLegend();
        l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP);
        l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER);
        l.setOrientation(Legend.LegendOrientation.HORIZONTAL);
        l.setDrawInside(true);
        l.setYOffset(20f);
        l.setXOffset(0f);
        l.setYEntrySpace(0f);
        l.setTextSize(8f);

        //X-axis
        XAxis xAxis = distinctionGraph.getXAxis();
        xAxis.setGranularity(1f);
        xAxis.setGranularityEnabled(true);
        xAxis.setCenterAxisLabels(true);
        xAxis.setDrawGridLines(false);
        xAxis.setAxisMaximum(3);
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
        xAxis.setValueFormatter(new IndexAxisValueFormatter(xVals));
        //Y-axis
        distinctionGraph.getAxisRight().setEnabled(false);
        YAxis leftAxis = distinctionGraph.getAxisLeft();
        leftAxis.setValueFormatter(new LargeValueFormatter());
        leftAxis.setDrawGridLines(true);
        leftAxis.setSpaceTop(35f);
        leftAxis.setAxisMinimum(0f);

I set 3 for groupCount but not working. Do I miss something?

For example, like this.

Answer: First, change variable like below.

    float barWidth = 0.3f;
    float barSpace = 0.0f;
    float groupSpace = 0.06f;
    int groupCount = 3;

The point was barWidth. I Changed it 0.0f to 0.3f.

0.3 * 3 = 0.9.

it's close to 1.

And, I modified groupBars like below.

distinctionGraph.groupBars(groupSpace, groupSpace, barSpace);

First Argument of groupBar is fromX. Before modification, groupSpace was 0.

This is screenshot after I solved it.