Skip to content

v.3.0.0-beta1 BarChart: ArrayIndexOutOfBoundsException when formatter applied, granularity is enabled and there's only one data point #2153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
antohama opened this issue Aug 15, 2016 · 21 comments

Comments

@antohama
Copy link

antohama commented Aug 15, 2016

Hi, I've faced an ArrayIndexOutOfBoundsException under following conditions:

  • AxisValueFormatter is applied to x axis, where formatted value is an item of a list;
  • granularity is enabled for x axis;
  • the data consists of only one value.

Here's the stacktrace:

java.lang.ArrayIndexOutOfBoundsException: length=12; index=-1
at java.util.ArrayList.get(ArrayList.java:310)
at com.example.asuprun.tempconverter.AnotherActivity$1.getFormattedValue(AnotherActivity.java:65)
at com.github.mikephil.charting.components.AxisBase.getFormattedLabel(AxisBase.java:451)
at com.github.mikephil.charting.components.AxisBase.getLongestLabel(AxisBase.java:437)
at com.github.mikephil.charting.renderer.XAxisRenderer.computeSize(XAxisRenderer.java:78)
at com.github.mikephil.charting.renderer.XAxisRenderer.computeAxisValues(XAxisRenderer.java:73)
at com.github.mikephil.charting.renderer.XAxisRenderer.computeAxis(XAxisRenderer.java:66)
at com.github.mikephil.charting.charts.BarLineChartBase.notifyDataSetChanged(BarLineChartBase.java:339)
at com.github.mikephil.charting.charts.Chart.setData(Chart.java:318)
at com.example.asuprun.tempconverter.AnotherActivity.plotData(AnotherActivity.java:88)
at com.example.asuprun.tempconverter.AnotherActivity.toggleData(AnotherActivity.java:98)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:270)
at android.view.View.performClick(View.java:4780)
at android.view.View$PerformClick.run(View.java:19866)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

Simplified code:

public class AnotherActivity extends AppCompatActivity {
    private ViewGroup chartView;
    private Map<String, Integer> data;
    private BarChart chart;

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

        chartView = (ViewGroup) findViewById(R.id.chart_view);
        chart = new BarChart(this);
        data = new LinkedHashMap<>();
        data.put("6/15", 36);
        data.put("8/15", 23);
        chartView.addView(chart);
        plotData();
    }

    private void plotData() {
        chart.clear();
        final ArrayList<String> xVals = new ArrayList<>();
        ArrayList<BarEntry> yVals = new ArrayList<>();

        for (Map.Entry<String, Integer> entry : data.entrySet()) {
            yVals.add(new BarEntry(xVals.size(), entry.getValue()));
            xVals.add(entry.getKey());
        }

        XAxis xAxis = chart.getXAxis();
        xAxis.setDrawGridLines(false);
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
        xAxis.setGranularityEnabled(true);
        xAxis.setValueFormatter(new AxisValueFormatter() {
                                    @Override
                                    public String getFormattedValue(float value, AxisBase axis) {
                                        try {
                                            return xVals.get((int) value);
                                        } catch (IndexOutOfBoundsException e) {
                                            e.printStackTrace();
                                            return "stub";
                                        }
                                    }

                                    @Override
                                    public int getDecimalDigits() {
                                        return 0;
                                    }
                                }
        );

        chart.getAxisRight().setEnabled(false);
        YAxis axisLeft = chart.getAxisLeft();
        axisLeft.setAxisMinValue(0f);
        axisLeft.setGranularityEnabled(true);
        axisLeft.setGranularity(1f);

        BarDataSet dataSet = new BarDataSet(yVals, "Data");
        BarData barData = new BarData(dataSet);
        barData.setBarWidth(0.7f);
        chart.setData(barData);
        chart.invalidate();
    }

    public void toggleData(View view) {
        if (data.size() > 1) {
            data.remove("8/15");
        } else {
            data.put("8/15", 23);
        }
        plotData();
    }
}
@PhilJay
Copy link
Owner

PhilJay commented Aug 15, 2016

The crash occurred in the code that you yourself wrote. The only one who can prevent this crash from happening is you yourself.

When using the formatters of course you need to be aware that the value coming from the formatter might not fit any array or list you use in the formatter.

Offtopic: As a personal recommendation I suggest to never simply place code into a try-catch block for preventing crashes. But this is more of a stackoverflow topic.

@PhilJay PhilJay closed this as completed Aug 15, 2016
@antohama
Copy link
Author

Hi Phil! Thank you for your comment!
In this case formatter rely on the data list, which the values for chart are taken from, too.
Thus it cannot be predicted that its value don't fit the list. The issue appears due to extra values on both edges of the list, which is handled by the library. Shouldn't it provide means then to take care of formatting of those extra values?

@levianye
Copy link

levianye commented Sep 6, 2016

I also meet such question,it seems label count be increased in the lib AxisRenderer.java in the function computeAxisValues,maybe we can rewrite it by self

@gao746700783
Copy link

i just solved this problem;
cause you used a custom valueFormatter,so,when reset chart data,you should reset your custom valueFormatter,it may help

@eprabhakar
Copy link

Well, I too got into the same problem, but with @PhilJay suggestion, I got a work around as follows

All you need to do is to change the method implementation from 

public String getFormattedValue(float value, AxisBase axis) {
            // "value" represents the position of the label on the axis (x or y)
             return mValues[(int) value];
}

To the following

public String getFormattedValue(float value, AxisBase axis) {
           // "value" represents the position of the label on the axis (x or y)
           if(mValues.length > (int) value) {
                return mValues[(int) value];
            } else return null;
 }

@PhilJay, please let me know if the above approach is OK.

@skh1993
Copy link

skh1993 commented Oct 28, 2016

@gao746700783 I want to know how do you solve? how to reset valueFormatter

@antohama
Copy link
Author

@skh1993 I think gao means calling setValueFormatter(null) on axis.

@skh1993
Copy link

skh1993 commented Oct 28, 2016

antohama
Did you solve this problem?

@antohama
Copy link
Author

@skh1993
yes, I'm checking whether the value is out of the list and return desired string in such case.

@ghost
Copy link

ghost commented Jan 20, 2017

The concern that @antohama has raised is genuine.

Thanks much @eprabhakar , your solution did work for me

@MatheusDinni
Copy link

Anyone having the issue yet? I'm facing the same problem and no suggestions above worked for me :/

@MatheusDinni
Copy link

@eprabhakar i changed your return statement from null to "" and worked for me.

@mkamals1989
Copy link

gao746700783 : How did you reset the value formatter? Could you pls share the code for that?

@santhosh285
Copy link

Thanks to @PhilJay for this great library 👍

Thanks @eprabhakar & @antohama 👏

The below code solved the crash.

xAxis.setValueFormatter(new IAxisValueFormatter() {
            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                if (value >= 0) {
                    if (mValues.length > (int) value) {
                        return mValues[(int) value];
                    } else return "";
                } else {
                    return "";
                }
            }
        });

NB: But it will not plot the line graph. So for the best result add a dummy value before it and make the data list size as two. And works all perfect.

@dujuanxian
Copy link

Same issue, without line graph.

@AndroPlus
Copy link

@santhosh285 it worked.. thanks

@oradkovsky
Copy link

Good lib but it's clearly lib's bug, not integrators bug.

@RFNadler
Copy link

Change: final ArrayList xVals = new ArrayList<>();
To: static ArrayList xVals = new ArrayList<>();

@abhay70
Copy link

abhay70 commented Jul 27, 2018

xAxis.setValueFormatter(new IAxisValueFormatter() {
@OverRide
public String getFormattedValue(float value, AxisBase axis) {
if (value >= 0) {
if (mValues.length > (int) value) {
return mValues[(int) value];
} else return "";
} else {
return "";
}
}
});

thanks it helped

@Alex-5290
Copy link

Thanks to @PhilJay for this great library 👍

Thanks @eprabhakar & @antohama 👏

The below code solved the crash.

xAxis.setValueFormatter(new IAxisValueFormatter() {
            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                if (value >= 0) {
                    if (mValues.length > (int) value) {
                        return mValues[(int) value];
                    } else return "";
                } else {
                    return "";
                }
            }
        });

NB: But it will not plot the line graph. So for the best result add a dummy value before it and make the data list size as two. And works all perfect.

This works!!! Thank You!

@wiztensai
Copy link

if you GONE the barchart then you set bardata, it can cause crash too! so, set visibility after you set bardata

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests