#android #indexoutofboundsexception #mpandroidchart
#Android #исключение indexoutofboundsexception #mpandroidchart
Вопрос:
Я разрабатываю приложение для составления бюджета, и в этом случае у меня есть класс статистики, в котором значения каждой группы расходов собираются и заполняются в столбцах с использованием MPAndroidChart. Идея заключается в том, что когда мы выбираем другой месяц в счетчике, значения, отображаемые на графике, обновляются соответствующим образом. Это работает правильно, когда количество категорий расходов текущего месяца совпадает с количеством категорий в выбранном новом месяце. С другой стороны, если количество категорий текущего месяца отличается от количества категорий в выбранном месяце, я получаю исключение ArrayIndexOutOfBoundsException.
Я уже создаю новый объект этого класса каждый раз, когда в счетчике выбирается другой месяц, поэтому мой вопрос в том, как я могу обновить массив mValues в myXAxisValueFormatter, чтобы избежать этого исключения?
Statistics.java
package com.robin.xbudget;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.formatter.IndexAxisValueFormatter;
import com.github.mikephil.charting.utils.ColorTemplate;
import java.time.LocalDate;
import java.time.Period;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Statistics extends Fragment {
private final String TAG = this.getClass().getSimpleName();
StatisticsListener callback;
private TextView mNumberIncomes;
private TextView mNumberExpenses;
private TextView mNumberDaysPassed;
private TextView mNumberDaysRemaining;
private LocalDate mLocalDateFirst;
private LocalDate mLocalDateLast;
private Spinner monthSpinner;
private BarChart mBarChart;
private BarDataSet mBarDataSet;
private BarData mData;
ArrayList<BarEntry> barEntries;
@RequiresApi(api = Build.VERSION_CODES.O)
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_statistics, container, false);
mNumberIncomes = (TextView) view.findViewById(R.id.number_incomes);
mNumberExpenses = (TextView) view.findViewById(R.id.number_expenses);
mNumberDaysPassed = (TextView) view.findViewById(R.id.days_passed_number);
mNumberDaysRemaining = (TextView) view.findViewById(R.id.days_remaining_number);
mLocalDateFirst = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
mLocalDateLast = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
mNumberIncomes.setText(String.valueOf(callback.getTotalIncomes()));
mNumberExpenses.setText(String.valueOf(callback.getTotalExpenses()));
mNumberDaysPassed.setText(String.valueOf(Period.between(mLocalDateFirst, LocalDate.now()).getDays()));
mNumberDaysRemaining.setText(String.valueOf(Period.between(LocalDate.now(), mLocalDateLast).getDays()));
monthSpinner = (Spinner) view.findViewById(R.id.spinner_month_stats);
monthSpinner.setAdapter(callback.getArrayAdapter());
monthSpinner.setSelection(callback.getSpinnerPosition());
monthSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
callback.setPeriodSelected((String) monthSpinner.getAdapter().getItem(position));
dataFiller();
mBarChart.invalidate();
Log.d(TAG, "onItemSelected called");
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
mBarChart = (BarChart) view.findViewById(R.id.bar_chart);
dataFiller();
return view;
}
public void dataFiller(){
mBarChart.setDrawBarShadow(false);
mBarChart.setDrawValueAboveBar(true);
mBarChart.setMaxVisibleValueCount(50);
mBarChart.setPinchZoom(false);
mBarChart.setDrawGridBackground(false);
barEntries = new ArrayList<>();
int x = 0;
for (String s : callback.getExpensesDataGroup()) {
float value = 0;
for (Transaction t : callback.getExpensesDataChild().get(s)) {
value = t.getQuantity();
}
barEntries.add(new BarEntry((float) x, value));
}
//This is the graphic
mBarDataSet = new BarDataSet(barEntries, "Expenses");
mBarDataSet.setColors(ColorTemplate.COLORFUL_COLORS);
mBarDataSet.notifyDataSetChanged();
mData = new BarData(mBarDataSet);
mData.setBarWidth(0.9f);
mBarChart.setData(mData);
String[] expenses = new String[callback.getExpensesDataGroup().size() 1];
expenses[0] = "Dummy";
for (int i = 1; i <= callback.getExpensesDataGroup().size(); i ) {
expenses[i] = callback.getExpensesDataGroup().get(i - 1);
}
Log.d(TAG, "Prior to mValues before: " expenses.length);
mBarChart.notifyDataSetChanged();
XAxis xAxis = mBarChart.getXAxis();
xAxis.setValueFormatter(new myXAxisValueFormatter(expenses));
xAxis.setPosition(XAxis.XAxisPosition.TOP);
xAxis.setGranularity(1f);
//xAxis.setCenterAxisLabels(true);
//xAxis.setAxisMinimum(1);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try{
callback = (StatisticsListener)context;
}catch(ClassCastException cce){
throw new ClassCastException("Class must implement StatisticListener");
}
}
class myXAxisValueFormatter extends IndexAxisValueFormatter {
private String[] mValues;
public myXAxisValueFormatter(String[]values) {
this.mValues = values;
}
@Override
public String getFormattedValue(float value) {
return mValues[(int)value];
}
}
interface StatisticsListener{
double getTotalIncomes();
double getTotalExpenses();
Map<String, List<Transaction>> getExpensesDataChild();
List<String> getExpensesDataGroup();
ArrayAdapter getArrayAdapter();
void setPeriodSelected(String periodSelected);
int getSpinnerPosition();
}
}
Вывод Logcat
2020-09-09 08:26:11.208 30608-30608/com.robin.xbudget D/Statistics: Prior to mValues before: 3
2020-09-09 08:26:11.373 30608-30608/com.robin.xbudget D/Statistics: Prior to mValues before: 3
2020-09-09 08:26:11.377 30608-30608/com.robin.xbudget D/Statistics: onItemSelected called
2020-09-09 08:26:13.002 30608-30608/com.robin.xbudget D/Statistics: Prior to mValues before: 2
2020-09-09 08:26:13.013 30608-30608/com.robin.xbudget E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.robin.xbudget, PID: 30608
java.lang.ArrayIndexOutOfBoundsException: length=2; index=2
at com.robin.xbudget.Statistics$myXAxisValueFormatter.getFormattedValue(Statistics.java:175)
at com.github.mikephil.charting.formatter.ValueFormatter.getAxisLabel(ValueFormatter.java:62)
at com.github.mikephil.charting.components.AxisBase.getFormattedLabel(AxisBase.java:488)
at com.github.mikephil.charting.components.AxisBase.getLongestLabel(AxisBase.java:474)
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:346)
at com.robin.xbudget.Statistics$1.onItemSelected(Statistics.java:86)
at android.widget.AdapterView.fireOnSelected(AdapterView.java:1366)
at android.widget.AdapterView.dispatchOnItemSelected(AdapterView.java:1355)
at android.widget.AdapterView.access$300(AdapterView.java:59)
at android.widget.AdapterView$SelectionNotifier.run(AdapterView.java:1314)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7050)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
Ответ №1:
вам нужно проверить значение в getFormattedValue, потому что оно может быть < 0 или >= mValues.длина
class myXAxisValueFormatter extends IndexAxisValueFormatter {
private String[] mValues;
public myXAxisValueFormatter(String[]values) {
this.mValues = values;
}
@Override
public String getFormattedValue(float value) {
int intValue = (int)value;
if (intValue < 0 || intValue >= mValues.length){
return "";
} else {
return mValues[intValue];
}
}
}