【发布时间】:2020-11-08 17:21:03
【问题描述】:
我有一个锻炼追踪器应用。
用户可以记录锻炼(例如重量、次数)。
它工作正常,但是当您在几个月之间更改然后修改记录的锻炼(例如删除/更新)或记录新的锻炼时,应用程序会崩溃。
我收到了concurrentModificationException。
我已经阅读了一些关于错误消息的文章,但是我仍然不知道为什么会收到错误消息。
为什么我会收到concurrentModificationException?
错误信息:
java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.size(ArrayList.java:1057)
at java.util.AbstractCollection.toArray(AbstractCollection.java:136)
at java.util.ArrayList.addAll(ArrayList.java:588)
at com.example.exerciseappv5.CalendarCode.CustomCalendarView.checkIfWeekCompleted(CustomCalView.java:181)
at com.example.exerciseappv5.CalendarCode.CustomCalendarView.lambda$ChangeMonth$0$CustomCalView(CustomCalView.java:167)
发生错误的行:
if (completedDates.equals("100")) {completedDatesList.addAll(thisWeekDates);}
CustomCalendarView(相关代码)
public class CustomCalendarView extends LinearLayout {
ArrayList<Date> dates = new ArrayList<>();
ArrayList<String> datesFormattedList = new ArrayList<>();
ArrayList<String> logDatesList = new ArrayList<>();
CopyOnWriteArrayList<String> completedDatesList = new CopyOnWriteArrayList<>
();
ArrayList<String> uniqueDatesWithLogEntries = new ArrayList<>();
ArrayList<List<String>> listOfWeeks = new ArrayList<>();
public CustomCalendarView(Context context) {
super(context);
}
public CustomCalendarView(final Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
InitializeLayout();
SetUpCalendar();
PreviousButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
calendar.add(Calendar.MONTH, -1);
SetUpCalendar();
}
});
NextButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
calendar.add(Calendar.MONTH, 1);
SetUpCalendar();
}
});
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setCancelable(true);
final String date = eventDateFormat.format(dates.get(position));
final ArrayList<String> arrayListDateFormattedList = (ArrayList<String>) datesFormattedList;
Intent i = new Intent(getContext(), WorkoutButtonsActivity.class);
i.putExtra(WorkoutButtonsActivity.EXTRA_DATE, date);
i.putStringArrayListExtra(WorkoutButtonsActivity.EXTRA_VISIBLE_DATES, arrayListDateFormattedList);
getContext().startActivity(i);
}
});
}
public CustomCalendarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void InitializeLayout() {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.calendar_layout, this);
NextButton = view.findViewById(R.id.nextBtn);
PreviousButton = view.findViewById(R.id.previousBtn);
CurrentDate = view.findViewById(R.id.current_Date);
gridView = view.findViewById(R.id.gridview);
}
void SetUpCalendar() {
datesFormattedList.clear();
String currentDate = dateFormat.format(calendar.getTime()); // e.g. July 2020
CurrentDate.setText(currentDate);
dates.clear();
Calendar monthCalendar = (Calendar) calendar.clone();
monthCalendar.set(Calendar.DAY_OF_MONTH, 1);
int FirstDayofMonth = monthCalendar.get(Calendar.DAY_OF_WEEK) - 2;
monthCalendar.add(Calendar.DAY_OF_MONTH, -FirstDayofMonth);
while (dates.size() < MAX_CALENDAR_DAYS) {
dates.add(monthCalendar.getTime());
monthCalendar.add(Calendar.DAY_OF_MONTH, 1);
}
/*CONVERTS THE LIST OF ALL VISIBLE DATES TO A STRING */
for (int i = 0; i < MAX_CALENDAR_DAYS; i++) {
final String dateFormatted = eventDateFormat.format(dates.get(i));
datesFormattedList.add(dateFormatted);
}
ChangeMonth();
}
public void ChangeMonth() {
completedDatesList.clear();
listOfWeeks.clear();
//Break Down month dates into weeks
listOfWeeks.add(datesFormattedList.subList(0, 7)); //1
listOfWeeks.add(datesFormattedList.subList(7, 14)); //2
listOfWeeks.add(datesFormattedList.subList(14, 21)); //3
listOfWeeks.add(datesFormattedList.subList(21, 28)); //4
listOfWeeks.add(datesFormattedList.subList(28, 35)); //5
listOfWeeks.add(datesFormattedList.subList(35, 42)); //6
// For loop query to check the percentage completion fo each week
for (int i = 0; i < listOfWeeks.size(); i++) {
List<String> thisWeekDates = listOfWeeks.get(i);
workoutCompletionPercentageViewModel = ViewModelProviders.of((FragmentActivity) context).get(WorkoutCompletionPercentageViewModel.class);
workoutCompletionPercentageViewModel.getWeekCompletionPercentage(thisWeekDates).observe((FragmentActivity) context,
completedDates -> checkIfWeekCompleted(completedDates, thisWeekDates)
);
}
//Observe data from log entry view model to determine whether calendar date has a log entry
logEntriesViewModel = ViewModelProviders.of((FragmentActivity) context).get(LogEntriesViewModel.class);
logEntriesViewModel.setFilter(datesFormattedList);
logEntriesViewModel.getDatesFilteredByMonth().observe((FragmentActivity) context, logDates -> setData(logDates));
}
public void checkIfWeekCompleted(String weekCompletion, List<String> thisWeekDates) {
if (weekCompletion.equals("100")) {
completedDatesList.addAll(thisWeekDates);
}
}
private void setData(List<String> logDates) {
logDatesList.clear();
logDatesList.addAll(logDates);
Set<String> setWithUniqueValues = new HashSet<>(logDatesList);
uniqueDatesWithLogEntries = new ArrayList<>(setWithUniqueValues);
myGridAdapter = new MyGridAdapter(context, dates, calendar, uniqueDatesWithLogEntries, completedDatesList);
gridView.setAdapter(myGridAdapter);
}
}
MyGridAdapter(我不确定这是否相关)
public class MyGridAdapter extends ArrayAdapter {
List<Date> dates;
CopyOnWriteArrayList<String> completedDates;
Calendar currentDate;
List<String> logs;
LayoutInflater inflater;
ImageView fire;
LayerDrawable bottomBorder;
private WorkoutCompletionPercentageViewModel workoutCompletionPercentageViewModel;
public MyGridAdapter(@NonNull Context context, List<Date> dates, Calendar currentDate, List<String> logs, CopyOnWriteArrayList<String> completedDates) {
super(context, R.layout.single_cell_layout);
this.completedDates = completedDates;
this.dates = dates;
this.currentDate = currentDate;
this.logs = logs;
inflater = LayoutInflater.from(context);
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Date monthDate = dates.get(position);
Calendar dateCalendar = Calendar.getInstance();
dateCalendar.setTime(monthDate);
int DayNo = dateCalendar.get(Calendar.DAY_OF_MONTH);
int displayMonth = dateCalendar.get(Calendar.MONTH) + 1;
int displayYear = dateCalendar.get(Calendar.YEAR);
int currentMonth = currentDate.get(Calendar.MONTH) + 1;
int currentYear = currentDate.get(Calendar.YEAR);
int currentDay = currentDate.get(Calendar.DAY_OF_MONTH);
View view = convertView;
if (view == null) {
view = inflater.inflate(R.layout.single_cell_layout, parent, false);
}
fire = view.findViewById(R.id.fire);
fire.setVisibility(View.INVISIBLE);
if (displayMonth == currentMonth && displayYear == currentYear) {
view.setBackgroundColor(getContext().getResources().getColor(R.color.green));
} else {
view.setBackgroundColor(Color.parseColor("#cccccc"));
}
ImageView calendarTick = view.findViewById(R.id.calendarTick);
Calendar eventCalendar2 = Calendar.getInstance();
// ArrayList<String> arrayList = new ArrayList<>();
for (int i = 0; i < logs.size(); i++) {
eventCalendar2.setTime(ConvertStringToDate(logs.get(i)));
if (DayNo == eventCalendar2.get(Calendar.DAY_OF_MONTH) && displayMonth == eventCalendar2.get(Calendar.MONTH) + 1
&& displayYear == eventCalendar2.get(Calendar.YEAR)) {
// arrayList.add(logs.get(i));
calendarTick.setVisibility(View.VISIBLE); // Instead of this we show a circle which was invisible
}
}
//SET THE CALENDAR BACKGROUND IF WEEK IS COMPLETED
Calendar eventCalendar3 = Calendar.getInstance();
// ArrayList<String> arrayList2 = new ArrayList<>();
for (int i = 0; i < completedDates.size(); i++) {
eventCalendar3.setTime(ConvertStringToDate(completedDates.get(i)));
if (DayNo == eventCalendar3.get(Calendar.DAY_OF_MONTH) && displayMonth == eventCalendar3.get(Calendar.MONTH) + 1
&& displayYear == eventCalendar3.get(Calendar.YEAR)) {
// arrayList2.add(completedDates.get(i));
//SETTING THE BORDER AROUND COMPLETED WEEKS
String backgroundHex;
fire.setVisibility(View.VISIBLE);
if (displayMonth == currentMonth && displayYear == currentYear) {
backgroundHex = "#1DB954";
} else {
backgroundHex = "#cccccc";
}
GradientDrawable border = new GradientDrawable();
bottomBorder = getBorders(
Color.parseColor(backgroundHex), // Background color
Color.parseColor("#ffd700"), // Border color
0, // Left border in pixels
5, // Top border in pixels
0, // Right border in pixels
5 // Bottom border in pixels
);
// Finally, apply the drawable as text view background
view.setBackground(bottomBorder);
}
}
Calendar eventCalendar = Calendar.getInstance();
TextView Day_Number = view.findViewById(R.id.calendar_day);
Day_Number.setText(String.valueOf(DayNo));
if (DayNo == currentDay && displayMonth == eventCalendar.get(Calendar.MONTH) + 1 && displayYear == eventCalendar.get(Calendar.YEAR)) {
Day_Number.setTextColor(Color.parseColor("#FFFF33"));
}
return view;
}
private Date ConvertStringToDate(String eventDate) {
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH);
Date date = null;
try {
date = format.parse(eventDate);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
@Override
public int getCount() {
return dates.size();
}
@Override
public int getPosition(@Nullable Object item) {
return dates.indexOf(item);
}
@Nullable
@Override
public Object getItem(int position) {
return dates.get(position);
}
// Custom method to generate one or multi side border for a view
protected LayerDrawable getBorders(int bgColor, int borderColor,
int left, int top, int right, int bottom) {
// Initialize new color drawables
ColorDrawable borderColorDrawable = new ColorDrawable(borderColor);
ColorDrawable backgroundColorDrawable = new ColorDrawable(bgColor);
// Initialize a new array of drawable objects
Drawable[] drawables = new Drawable[]{
borderColorDrawable,
backgroundColorDrawable
};
LayerDrawable layerDrawable = new LayerDrawable(drawables);
layerDrawable.setLayerInset(
1, // Index of the drawable to adjust [background color layer]
left, // Number of pixels to add to the left bound [left border]
top, // Number of pixels to add to the top bound [top border]
right, // Number of pixels to add to the right bound [right border]
bottom // Number of pixels to add to the bottom bound [bottom border]
);
return layerDrawable;
}
}
【问题讨论】:
-
很可能
MyGridAdapter在您修改它时在completedDatesList上打开了一个迭代器。 -
如何识别导致错误的原因?
-
堆栈跟踪告诉您确切地 在哪里发生错误:
CustomCalendarView.checkIfWeekCompleted(CustomCalView.java:181)。从那里开始。 -
完成日期列表的结束类型是什么?
-
CopyOnWriteArrayList
completedDatesList = new CopyOnWriteArrayList();
标签: java android collections