我正在编程一个JToggleButton
,以将元素的配置(望远镜配置)加载到内存中/从内存中丢弃,所以我在JFrame
中添加了一个JComboBox
,并在其附近添加了加载所选项目的按钮。当选择JToggleButton
时,会显示一个硬盘图标,否则会显示另一个图标。我使用的是IntelliJ IDEA GUI编辑器。当然,我在这个按钮上添加了一个ItemListener
(如网络上所建议的):
loadTelescopeButton.setSelected(true);
System.out.println(loadTelescopeButton.isSelected());
loadTelescopeButton.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
System.out.println("LAODACTION " + loadTelescopeButton.isSelected());
try {
if (e.getStateChange() == ItemEvent.SELECTED) {
String selected = telescopesList.getSelectedItem().toString();
if ((selected != null) && (!selected.equals("")) && (ObjUtils.isAlphaNumeric(selected))) {
//...
} else {
showErrorMessage("Invalid id selected!");
}
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
if ((configurationActivity != null) && (configurationActivity.getManager() != null) &&
(configurationActivity.getTelescope() != null) && (configurationActivity.getTelescope().isConnected())) {
//...
} else {
//...
}
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
输出:true
->显示窗口时LAOD_ACTION false
-->单击按钮时
我用一些新的切换按钮进行了一些测试,它们给了我同样的错误:itemStateChanged(ItemEvent e) {...}
中的代码永远重复,没有停止!在这段代码中没有for
和while
循环!结果是出现了大量的消息对话框(应该只显示一个对话框),如果我在桌面上聚焦另一个窗口,对话框后面的屏幕就会变成黑色(父窗口的区域)。我将侦听器更改为ActionListener
,现在所有内容都执行一次/单击。
为什么会出现此错误?我从中复制了该代码https://stackoverflow.com/a/7524627/6267019,正如您所看到的。
GitHub上的完整代码在这里,我强调了切换按钮的代码。同样的错误也发生在我的MainActivity.java
文件中的其他JToggleButton
上,而且当调试IntelliJ时,我可以看到侦听器中的代码永远重复。在数千个对话框之后,Windows向我显示了一条消息,并关闭了Java平台二进制文件,并出现错误。
编辑:
新类中的相同问题:
import javax.swing.*;
import java.awt.*;
public class ErrorGUI extends JFrame {
public ErrorGUI() throws HeadlessException {
super("ciao");
JPanel panel1 = new JPanel();
setContentPane(panel1);
JToggleButton ciaoToggleButton = new JToggleButton("cajs");
ciaoToggleButton.setSelected(true);
ciaoToggleButton.addItemListener(e -> {
System.out.println("caiooasfsdvn");
try {
JOptionPane.showMessageDialog(panel1, "skjngksfnb");
} catch (Exception e2) {
e2.printStackTrace();
}
});
panel1.add(ciaoToggleButton);
pack();
setVisible(true);
}
public static void main(String[] args) {
new ErrorGUI();
}
}
无论何时打开模式对话框,只有在对话框关闭后,打开方法调用才会返回。这对于返回输入值或选项的对话框至关重要。
这意味着,当对话框打开时,必须启动一个新的事件处理循环来对对话框中的输入做出反应。
因此,当您从侦听器打开模式对话框时,您将停止处理当前事件并开始处理后续事件,这可能会严重干扰当前事件的处理。最值得注意的是,当打开新对话框时,该按钮会突然失去焦点。
通过将侦听器更改为,可以很容易地演示嵌套事件处理
ciaoToggleButton.addItemListener(e -> {
System.out.println("entering");
JOptionPane.showMessageDialog(panel1,
e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected");
System.out.println("leaving");
});
它将打印的序列
entering
entering
leaving
leaving
显示了在未完成对旧事件的处理时如何生成矛盾事件。
正如其他人所说,你可以通过在完成偶数处理后打开对话框来解决这个问题,比如
ciaoToggleButton.addItemListener(e -> {
System.out.println("entering");
EventQueue.invokeLater(() -> JOptionPane.showMessageDialog(panel1,
e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected"));
System.out.println("leaving");
});
或者强制执行非模态对话框:
ciaoToggleButton.addItemListener(e -> {
System.out.println("entering");
JDialog d = new JOptionPane(
e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected",
JOptionPane.INFORMATION_MESSAGE)
.createDialog(panel1, UIManager.getString("OptionPane.messageDialogTitle"));
d.setModal(false);
d.setVisible(true);
System.out.println("leaving");
});
(在实际应用程序中,您可以保留对话框以备以后重用,也可以在使用后调用dispose
)
不幸的是,文档中没有充分强调打开模式对话框(或做任何其他创建次要事件循环的事情)的危险。您可以从任何地方了解到,从其他线程访问Swing组件可能会造成不一致,但在有未完全处理的事件时启动新的事件处理循环可能会产生类似的影响。
我不能说我理解你的代码为什么行为不端,但我同意你所看到的不太有意义,这可能是由于JOptionPane调用在某种程度上影响了JToggleButton的状态更改。解决这一问题的一种方法是将JOptionPane调用封装在Runnable中,并通过SwingUtilities.invokeLater(...)
在Swing事件队列中对其进行排队。例如:
import javax.swing.*;
import java.awt.*;
@SuppressWarnings("serial")
public class ErrorGUI extends JFrame {
public ErrorGUI() throws HeadlessException {
super("ciao");
JPanel panel1 = new JPanel();
setContentPane(panel1);
JToggleButton ciaoToggleButton = new JToggleButton("cajs");
ciaoToggleButton.setSelected(true);
ciaoToggleButton.addItemListener(e -> {
System.out.println("caiooasfsdvn");
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(panel1, "skjngksfnb");
});
// JOptionPane.showMessageDialog(panel1, "skjngksfnb");
});
panel1.add(ciaoToggleButton);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new ErrorGUI();
});
}
}
一个有趣的变体:
ciaoToggleButton.setSelected(true);
System.out.println("0:" + ciaoToggleButton.isSelected());
ciaoToggleButton.addItemListener(e -> {
System.out.println("1: " + ciaoToggleButton.isSelected());
if (e.getStateChange() == ItemEvent.SELECTED) {
JOptionPane.showMessageDialog(panel1, "skjngksfnb");
}
System.out.println("2: " + ciaoToggleButton.isSelected());
});
打印输出:
0:true
1: false
2: false
1: true
1: false
2: false
2: false
1: true
1: false
2: false
2: false