I am hiding a view via setVisibility(View.INVISIBLE)
. Later on when I try to show the view again in a different method via setVisibility(View.VISIBLE)
I get the following exception
03-28 01:32:05.450: E/AndroidRuntime(20895): FATAL EXCEPTION: main
03-28 01:32:05.450: E/AndroidRuntime(20895): java.util.ConcurrentModificationException
03-28 01:32:05.450: E/AndroidRuntime(20895): at java.util.HashMap$HashIterator.nextEntry(HashMap.java:796)
03-28 01:32:05.450: E/AndroidRuntime(20895): at java.util.HashMap$KeyIterator.next(HashMap.java:823)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:946)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewRoot.handleDragEvent(ViewRoot.java:3027)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewRoot.handleMessage(ViewRoot.java:2185)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.os.Handler.dispatchMessage(Handler.java:99)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.os.Looper.loop(Looper.java:132)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.app.ActivityThread.main(ActivityThread.java:4028)
03-28 01:32:05.450: E/AndroidRuntime(20895): at java.lang.reflect.Method.invokeNative(Native Method)
03-28 01:32:05.450: E/AndroidRuntime(20895): at java.lang.reflect.Method.invoke(Method.java:491)
03-28 01:32:05.450: E/AndroidRuntime(20895): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
03-28 01:32:05.450: E/AndroidRuntime(20895): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
03-28 01:32:05.450: E/AndroidRuntime(20895): at dalvik.system.NativeStart.main(Native Method)
When I comment out the line that changes the visibility back to visible, I don't get the exception.
I first thought that the exception would be caused by some other code iterating through a hashmap, however, I don't do any modifications while iterating through the hashmaps I use, neither do I have multithreading, which seem to be the most common reason for this exception. Also I don't get the exception when I don't change back the visibility.
EDIT:
The exception occurs in a custom fragment. Below is the code where I iterate over the hashmap (mWidgetConfig
) that contains information about the configuration of custom widgets that I am trying to restore. The hashmap is a public variable in the fragment.
In an OnDragListener
which is created by the fragment, I update the hashmap according to a certain drag operation, like this:
// Update the widget configuration of the fragment that created this listener
mFragment.mWidgetConfig.put(startCircleTag, "0");
I also iterate over the hashmap to check a certain condition but I don't do any modification during the iteration:
Iterator<String> keySetItr = mFragment.mWidgetConfig.keySet().iterator();
while(keySetItr.hasNext()) {
String tag = keySetItr.next();
if(mFragment.mWidgetConfig.get(tag).equals((String) destSocket.getTag())) {
// do something, though no modification of the hashmap
break;
}
}
In addition I do one iteration in the fragment itself while trying to restore the widget configuration. Below is the code I use to configure the widget according to the hashmap:
public void configureWidgets() {
resetWidgets();
Iterator<String> keySetItr = mWidgetConfig.keySet().iterator();
while(keySetItr.hasNext()) {
String tag = keySetItr.next();
Integer value = Integer.parseInt(mWidgetConfig.get(tag));
ImageView destSocket = null;
switch(value) {
case 0:
// The circle will not be connected to any socket
continue;
case 1:
destSocket = mSocket1;
break;
case 2:
destSocket = mSocket2;
break;
case 3:
destSocket = mSocket3;
break;
}
ImageView startCircle = (ImageView) mLayout.findViewWithTag(tag);
ImageView startPlug = (ImageView) mLayout.findViewWithTag(tag + "_plug");
// Replace the drawable of destSocket
destSocket.setBackgroundDrawable(getActivity().getResources().getDrawable(R.drawable.socket_plugged));
// Hide plug view
startPlug.setVisibility(View.INVISIBLE);
// Draw a line between the start circle view and the destination socket view
mConnectionLinesView.addLine(startCircle, destSocket);
}
}
public void resetWidgets() {
// Remove all lines
mConnectionLinesView.removeLines();
// Show all eventually previously hidden plugs
//mPlug1.setVisibility(View.VISIBLE);
//mPlug2.setVisibility(View.VISIBLE);
//mPlug3.setVisibility(View.VISIBLE);
// Set to backround drawable of the socket to the initial one
mSocket1.setBackgroundDrawable(getActivity().getResources().getDrawable(R.drawable.socket).mutate());
mSocket2.setBackgroundDrawable(getActivity().getResources().getDrawable(R.drawable.socket).mutate());
mSocket3.setBackgroundDrawable(getActivity().getResources().getDrawable(R.drawable.socket).mutate());
}
As soon as the lines that set the visibility of the "plugs" above are used in the code, I get the exception.
SOLUTION
The reason the exception got thrown is that I called the configuration methods in the DragEvent.ACTION_DRAG_ENDED
case statement of the OnDragListener
. When I put the same code into the DragEvent.ACTION_DROP
case statement the exception doesn't get thrown. No clue why. Thanks for your help guys
As I understand this is caused by ViewGroup
implementation details. And it's not connected with multithreading.
When drag starts ViewGroup
creates a HashSet of child views that must be notified about ACTION_DRAG_ENDED
event. This is a set of visible children. When child visibility is changed a corresponding ViewGroup
modifies that set (adding a child if its visibility is VISIBLE
). And in your case it happens during iterations over that collection.
Think, the easiest solution for you is to postpone the visibility change.
view.post(new Runnable() {
public void run() {
view.setVisibility(View.VISIBLE);
}
});
And exception does not happen when you put your code into the ACTION_DROP
case statement because that set is not being iterated at the moment you change the view visibility.
For details see ViewGroup.dispatchDragEvent(DragEvent) source code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With