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)