JToggleButton addItemListener 似乎永远重复 ItemListener [英] JToggleButton addItemListener seems to repeat the ItemListener forever

查看:36
本文介绍了JToggleButton addItemListener 似乎永远重复 ItemListener的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个 JToggleButton 来加载到/从内存中丢弃一个元素的配置(望远镜配置),所以我添加了一个 JComboBoxJFrame 和它附近的按钮以加载所选项目.选择<代码> JToggleButton 时,将显示硬盘图标,如果否则,则另一个图标.为此,我正在使用 IntelliJ IDEA GUI 编辑器.当然,我已经向该按钮添加了一个 ItemListener(根据网络的建议):

I'm programming a JToggleButton to load to/discard from memory the configuration of an element (a telescope config), so I've added a JComboBox in a JFrame and near it the button to load the selected item. When the JToggleButton is selected, an hard disk icon is displayed, another icon if otherwise. I'm using the IntelliJ IDEA GUI editor for that. Of course, I've added an ItemListener (as suggested from the web) to that button:

    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();
            }
        }
    });

输出:

-> 窗口显示时
LAOD_ACTION false
-> 当我点击按钮

Output:
true
-> When the window is displayed
LAOD_ACTION false
-> When I click the button

我用一些新的切换按钮做了一些测试,他们给了我同样的错误:itemStateChanged(ItemEvent e) {...} 中的代码永远重复,没有停止!在那段代码中没有 forwhile 循环!结果是大量的消息对话框(应该只显示一个对话框),如果我将另一个窗口放在桌面上,对话框后面的屏幕会变黑(父窗口的区域).我将侦听器更改为 ActionListener,现在所有内容都执行一次/单击.

I've made some tests with some new toggle buttons and they gave me same error: the code inside itemStateChanged(ItemEvent e) {...} is repeated forever, without stopping! In that piece of code there are no for and while loops! The result is a great number of message dialogs (only one dialog should be displayed), and if I focus another window in my desktop the screen behind the dialogs becomes black (the area of the parent window). I changed the listener to ActionListener and now everything is executed one time/click.

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

Why this error? I've copied that code from https://stackoverflow.com/a/7524627/6267019, as you can see.

GitHub 上的完整代码 此处,我已突出显示该切换按钮的代码.我的 MainActivity.java 文件中的其他 JToggleButton 也会发生同样的错误,而且在调试 IntelliJ 时,我会看到侦听器中的代码永远重复.经过数千次对话后,Windows 向我显示一条消息并关闭 Java 平台二进制文件并显示错误.

Full code on GitHub Here, I've highlighted the code for that toggle button. The same error happens with other JToggleButtons in my MainActivity.java file, and also when debugging IntelliJ lets me see that the code in the listener is repeated forever. After some thousand of dialogs Windows shows me a message and closes Java Platform Binary with an error.


新类中同样的问题:


The same problem in a new class:

 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();
     }
 }

推荐答案

无论何时打开模态对话框,打开方法调用只会在对话框关闭后返回.这对于返回输入值或选项的对话框至关重要.

Whenever you open a modal dialog, the opening method call will return only after the dialog has been closed. This is crucial for the dialogs that return an entered value or choice.

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

This implies that while the dialog is open, a new event handling loop has to be started to react on the input in the dialog.

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

So when you open a modal dialog from a listener, you are stopping the handling of the current event and start processing of subsequent events, which can disturb the handling of the current event significantly. Most notably, the button will suddenly loose the focus when the new dialog is opened.

通过将监听器更改为

ciaoToggleButton.addItemListener(e -> {
    System.out.println("entering");
    JOptionPane.showMessageDialog(panel1,
       e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected");
    System.out.println("leaving");
});

这将打印

entering
entering
leaving
leaving

显示在旧事件的处理尚未完成时如何生成矛盾事件.

showing how the contradicting event is generated while the processing of the old one hasn’t completed.

正如其他人所说,您可以通过在偶数处理完成后打开对话框来解决此问题,例如

As said by others, you can fix this by opening the dialog after the completion of the even handling, like

ciaoToggleButton.addItemListener(e -> {
    System.out.println("entering");
    EventQueue.invokeLater(() -> JOptionPane.showMessageDialog(panel1,
       e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected"));
    System.out.println("leaving");
});

或者您强制使用非模态对话框:

or you enforce a non-modal dialog:

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)

(in a real application you would either keep the dialog for later reuse or call dispose after use)

不幸的是,打开模态对话框(或做任何其他会创建次要事件循环) 在文档中没有得到足够的强调.从其他线程访问 Swing 组件可能会造成不一致,但在有未完全处理的事件时启动新的事件处理循环可能会产生类似的影响.

Unfortunately, the danger of opening modal dialogs (or doing anything else that creates a secondary event loop) hasn’t been emphasized enough in the documentation. You can read everywhere that accessing Swing components from other threads can create inconsistencies, but starting new event handling loop while there are incompletely processed events can have a similar impact.

这篇关于JToggleButton addItemListener 似乎永远重复 ItemListener的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆