Android – setVisibility resultados en java.util.ConcurrentModificationException
Estoy ocultando una vista a través de setVisibility(View.INVISIBLE)
. Más adelante, cuando intento mostrar la vista de nuevo en un método diferente a través de setVisibility(View.VISIBLE)
, obtengo la siguiente excepción
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)
Cuando comento la línea que cambia la visibilidad de nuevo a visible, no obtengo la excepción.
- Spinner se mueve hacia abajo en el diseño después de seleccionar un elemento
- Estado lista estado desplegable y desactivado
- Android ImageView escala tipo centerCrop
- Cambiar altura de línea / línea en EditText en un nivel de carácter
- Android: Visualice TextView en el lado derecho de EditText que está en TextInputLayout
Pensé primero que la excepción sería causada por algún otro código iterating a través de un hashmap, sin embargo, no hago ninguna modificación mientras que iterating a través de los hashmaps que utilizo, tampoco tengo multithreading, que parece ser la razón más común para Esta excepción. También no consigo la excepción cuando no cambio detrás la visibilidad.
EDIT :
La excepción se produce en un fragmento personalizado. A continuación se muestra el código en el que iterar sobre el hashmap ( mWidgetConfig
) que contiene información acerca de la configuración de widgets personalizados que estoy intentando restaurar. El hashmap es una variable pública en el fragmento.
En un OnDragListener
que es creado por el fragmento, actualizar el hashmap de acuerdo con una operación de arrastre determinado, como esto:
// Update the widget configuration of the fragment that created this listener mFragment.mWidgetConfig.put(startCircleTag, "0");
También iterate sobre el hashmap para comprobar una cierta condición pero no hago ninguna modificación durante la iteración:
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; } }
Además hago una iteración en el fragmento mismo mientras intento restaurar la configuración del widget. A continuación se muestra el código que utilizo para configurar el widget según el 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()); }
Tan pronto como las líneas que fijan la visibilidad de los "enchufes" arriba se utilizan en el código, consigo la excepción.
SOLUCIÓN
La razón por la excepción se lanzó es que he llamado a los métodos de configuración en la declaración de caso DragEvent.ACTION_DRAG_ENDED
de OnDragListener
. Cuando pongo el mismo código en la declaración de caso DragEvent.ACTION_DROP
la excepción no se lanza. Ninguna pista por qué. Gracias por su ayuda chicos
- ¿Cuáles son las diferencias entre LinearLayout, RelativeLayout y AbsoluteLayout?
- Cómo agregar rectángulos encima del rectángulo existente en el lienzo
- Eliminar la sombra debajo de AppBarLayout widget android
- ¿Cómo puedo implementar una Lista de Expandir / Contraer el Material de Diseño en Android?
- ¿Por qué no usar siempre android: configChanges = "keyboardHidden | orientation"?
- Android: establece una vista para estar encima de los elementos dibujados con lienzo
- Control de dos ViewPager juntos
- Diseño de dos columnas con la primera columna wrap_content, second fill_parent
Según entiendo, esto se debe a los detalles de implementación de ViewGroup
. Y no está conectado con multithreading.
Cuando arranca arranca ViewGroup
crea un HashSet de vistas secundarias que debe ser notificado sobre el evento ACTION_DRAG_ENDED
. Este es un conjunto de niños visibles. Cuando se cambia la visibilidad de un niño, un grupo de ViewGroup
correspondiente modifica dicho conjunto (agregando un niño si su visibilidad es VISIBLE
). Y en su caso sucede durante iteraciones sobre esa colección.
Piense, la solución más fácil para usted es posponer el cambio de visibilidad.
view.post(new Runnable() { public void run() { view.setVisibility(View.VISIBLE); } });
Y la excepción no sucede cuando pones tu código en la ACTION_DROP
caso porque ese conjunto no está siendo iterado en el momento que cambias la visibilidad de la vista.
Para obtener más información, consulte el código fuente ViewGroup.dispatchDragEvent (DragEvent) .
Trate de usar:
setVisibility(View.GONE); setVisibility(View.VISIBLE);
Otra posible solución es envolver su vista arrastrable en algún ViewGroup
(por ejemplo, FrameLayout
) y mantenerlo visible todo el tiempo.
De esta manera sólo desaparecerá la vista arrastrada contenida dentro de ella, dejando un agujero donde estaba (igual que antes), pero el padre del envoltorio no recibirá notificación de que el objeto arrastrable está oculto.
Esencialmente pasar de
ViewGroup:parent ┗ View:draggable (toggling setVisible on this one, parent gets notified)
a
ViewGroup:parent ┗ ViewGroup:wrapper (setVisible never called on this one) ┗ View:draggable (toggling setVisible on this one, wrapper gets notified)
La ConcurrentModificationException
se evita aquí porque el Map
problemático contiene solamente un elemento, por lo tanto una iteración se hace, que significa una llamada a .next
/ .next
.
Decide por ti mismo si se trata de un hack 🙂
Nota: su edición no tiene nada que ver con la pregunta, ya que la excepción se trata de ViewGroup.mDragNotifiedChildren
y no de su Map
(consulte stacktrace),