Android-setVisibility导致java.util.ConcurrentModificationException

Android-setVisibility导致java.util.ConcurrentModificationException,android,android-layout,Android,Android Layout,我正在通过设置可见性(view.INVISIBLE)隐藏视图。稍后,当我试图通过setVisibility(view.VISIBLE)以不同的方法再次显示视图时,我得到了以下异常 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

我正在通过
设置可见性(view.INVISIBLE)
隐藏视图。稍后,当我试图通过
setVisibility(view.VISIBLE)
以不同的方法再次显示视图时,我得到了以下异常

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)
当我注释掉将可见性更改回可见的行时,我没有得到异常

我首先认为异常可能是由一些其他代码在hashmap中迭代引起的,但是,在迭代我使用的hashmap时,我没有做任何修改,也没有多线程,这似乎是导致此异常的最常见原因。而且,当我不更改回可见性时,也不会出现异常

编辑
异常发生在自定义片段中。下面是我在hashmap(
mWidgetConfig
)上迭代的代码,该hashmap包含有关我试图恢复的自定义小部件的配置的信息。hashmap是片段中的一个公共变量

在片段创建的
OnDragListener
中,我根据特定的拖动操作更新hashmap,如下所示:

// Update the widget configuration of the fragment that created this listener
                mFragment.mWidgetConfig.put(startCircleTag, "0");
我还迭代hashmap以检查特定条件,但在迭代过程中不做任何修改:

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;

            }
        }
迭代器keySetItr=mf片断.mWidgetConfig.keySet().Iterator(); while(keySetItr.hasNext()){ String tag=keySetItr.next(); if(mffragment.mWidgetConfig.get(tag).equals((String)destSocket.getTag()){ //做点什么,尽管不修改hashmap 打破 } } 此外,在尝试恢复小部件配置时,我在片段本身中进行了一次迭代。下面是我用来根据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());
}
public void configureWidgets(){
resetWidgets();
迭代器keySetItr=mWidgetConfig.keySet().Iterator();
while(keySetItr.hasNext()){
String tag=keySetItr.next();
整数值=Integer.parseInt(mWidgetConfig.get(tag));
ImageView destSocket=null;
开关(值){
案例0:
//圆圈将不会连接到任何插座
继续;
案例1:
destSocket=mSocket1;
打破
案例2:
destSocket=mSocket2;
打破
案例3:
destSocket=mSocket3;
打破
}
ImageView startCircle=(ImageView)mLayout.findViewWithTag(标记);
ImageView startPlug=(ImageView)mLayout.findViewWithTag(tag+“_plug”);
//更换插座的可拉拔部分
destSocket.setBackgroundDrawable(getActivity().getResources().getDrawable(R.drawable.socket_));
//隐藏插件视图
startPlug.setVisibility(视图不可见);
//在起始圆视图和目标套接字视图之间绘制一条线
mConnectionLinesView.addLine(startCircle,destSocket);
}
}
公共void resetWidgets(){
//删除所有线路
mConnectionLinesView.removeLines();
//显示所有先前隐藏的插头
//mPlug1.setVisibility(View.VISIBLE);
//mPlug2.setVisibility(View.VISIBLE);
//mPlug3.setVisibility(View.VISIBLE);
//设置为将插座的可拉拔部分倒圆至初始位置
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());
}
一旦代码中使用了设置上述“plugs”可见性的行,我就会得到异常

解决方案
引发异常的原因是我调用了
OnDragListener
DragEvent.ACTION\u DRAG\u end
case语句中的配置方法。当我将相同的代码放入
DragEvent.ACTION\u DROP
case语句时,不会抛出异常。不知道为什么。感谢您的帮助,伙计们

尝试使用:

setVisibility(View.GONE);
setVisibility(View.VISIBLE);

据我所知,这是由
ViewGroup
实现细节引起的。它与多线程无关

当拖动开始时,
ViewGroup
创建一组子视图,必须通知这些子视图有关
ACTION\u drag\u end
事件。这是一组可见的子对象。更改子级可见性时,相应的
视图组
会修改该集合(如果其可见性为
可见,则添加子级
)。在您的例子中,它发生在该集合的迭代过程中

想想看,对您来说最简单的解决方案是推迟可见性更改

view.post(new Runnable() {
  public void run() {
    view.setVisibility(View.VISIBLE);
  }
});
当您将代码放入
ACTION\u DROP
case语句中时,不会发生异常,因为在您更改视图可见性时,该集合没有被迭代


有关详细信息,请参阅源代码。

另一种可能的解决方案是将可拖动视图包装在一些
视图组中(例如
框架布局
),并始终保持可见

这样,只有包含在其中的可拖动视图将消失,并在原来的位置留下一个洞(与以前一样),但包装器的父级不会收到隐藏可拖动视图的通知

本质上从

ViewGroup:parent
 ┗ View:draggable (toggling setVisible on this one, parent gets notified)

这里避免了
ConcurrentModificationException
,因为有问题的
映射只包含一个元素,因此完成了一次迭代,这意味着对
迭代器的一次调用。hasNext
/
.next

如果这是黑客行为,请自己决定:)


注意:您的编辑与问题无关,因为异常是关于
ViewGroup.mDragNotifiedChildren
而不是您的
Map
(请参见stacktrace),

发布您的代码以迭代hashmap…并创建
ViewGroup:parent
 ┗ ViewGroup:wrapper (setVisible never called on this one)
     ┗ View:draggable (toggling setVisible on this one, wrapper gets notified)