JToggleButton addItemListener似乎永远重复ItemListener

时间:2017-01-06 16:43:21

标签: java swing itemlistener gui-builder jtogglebutton

我正在编程JToggleButton以加载/丢弃内存中元素的配置(望远镜配置),因此我在{{{{}}中添加了JComboBox 1}}并且在它附近加载所选项目的按钮。选择JFrame时,将显示硬盘图标,否则显示另一个图标。我正在使用IntelliJ IDEA GUI编辑器。当然,我已经在该按钮上添加了JToggleButton(根据网络建议):

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,现在所有内容都执行一次/单击。

为什么会出现这个错误?我们已经从https://stackoverflow.com/a/7524627/6267019复制了该代码,如您所见。

GitHub上的完整代码Here,我突出显示了该切换按钮的代码。我的ActionListener文件中的其他JToggleButton也发生了同样的错误,并且在调试IntelliJ时也让我看到监听器中的代码会永远重复。在几千个对话框之后,Windows向我显示一条消息并关闭Java Platform Binary并显示错误。

编辑:
新课程中的同样问题:

MainActivity.java

2 个答案:

答案 0 :(得分:3)

我不能说我理解为什么你的代码行为不端,但我同意你所看到的内容并不合理,可能是由于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

答案 1 :(得分:3)

每当打开模态对话框时,只有在关闭对话框后才会返回打开方法调用。这对于返回输入值或选择的对话框至关重要。

这意味着当对话框打开时,必须启动一个新的事件处理循环来对对话框中的输入做出反应。

因此,当您从侦听器打开模式对话框时,您将停止处理当前事件并开始处理后续事件,这可能会显着干扰当前事件的处理。最值得注意的是,当打开新对话框时,按钮会突然失去焦点。

通过将侦听器更改为

,可以轻松演示嵌套事件处理
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

不幸的是,在文档中没有充分强调打开模态对话框(或执行任何其他创建secondary event loop)的危险。您可以随处读取从其他线程访问Swing组件可能会产生不一致的情况,但是在存在未完成处理的事件时启动新的事件处理循环可能会产生类似的影响。