Java 我的JDialog有时会收到来自调用应用程序的冗余击键?(提供代码)

Java 我的JDialog有时会收到来自调用应用程序的冗余击键?(提供代码),java,multithreading,swing,netbeans-platform,Java,Multithreading,Swing,Netbeans Platform,我在基于Netbeans RCP 8.2的java(8)中开发了一个复杂的音乐应用程序,我遇到了一个随机出现的奇怪问题 我有一个带有面板的JFrame,其中包含许多jc组件。我使用面板的InputMap/ActionMap来捕获“a”、“b”、…、“g”按键并调用操作 该操作检索键char,然后显示一个JDialog,其中包含一个用于编辑某些文本数据的JTextField 在使用dialog.setVisible(true)显示对话框之前,操作将调用dialog.prepare(char键),以

我在基于Netbeans RCP 8.2的java(8)中开发了一个复杂的音乐应用程序,我遇到了一个随机出现的奇怪问题

我有一个带有面板的JFrame,其中包含许多jc组件。我使用面板的InputMap/ActionMap来捕获“a”、“b”、…、“g”按键并调用操作

该操作检索键char,然后显示一个JDialog,其中包含一个用于编辑某些文本数据的JTextField

在使用dialog.setVisible(true)显示对话框之前,操作将调用dialog.prepare(char键),以便JDialog可以在显示之前进行初始化。实际上,dialog.prepare(char键)只在jtext字段中追加传递的字符(转换为大写)

这在大多数情况下都有效:例如,我在JFrame中按“c”,然后JDialog在JTextField的末尾显示为“c”

但有时,可能是1/20次,我在JTextfield的末尾得到“Cc”

这就像原始的按键事件(来自JFrame面板中的JComponent,使用InputMap/ActionMap处理)也被JDialog冗余处理

我确认这不是键盘硬件问题。我用Win8在第二台计算机上复制了这个问题(我的是Win10)

我尝试使用KeyListener而不是InputMap/ActionMap,但没有成功 和2/使用java.awt.EventQueue.invokeLater()将密钥字符追加到JTextField中

我创建了一个独立的小应用程序(见下文)来重现问题并促进调试……但这个小应用程序运行良好,我无法重现问题:-(然后我再次与我的真实应用程序代码进行比较,它实际上是相同的代码,只是真实应用程序是一个完整的Netbeans RCP应用程序

那么,Netbeans RCP会影响Swing处理关键事件的方式吗?我觉得很奇怪

我迷路了,任何提示/建议的测试都将不胜感激

/**
 * Try to reproduce double key problem... Failed because this works OK !! :-(
 */
public class PbKeyDouble extends JFrame {

   MyDialog dialog;

   public static void main(String[] args) {
      javax.swing.SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            PbKeyDouble o = new PbKeyDouble();
            o.setVisible(true);
         }
      });
   }

   public PbKeyDouble() {
      // GUI INITIALIZATION

      // Add a basic panel
      JPanel panel = new JPanel();
      getContentPane().add(panel);
      panel.setPreferredSize(new Dimension(300, 200));

      JButton button = new JButton("BUTTON");
      panel.add(button);
      // Button not used, it's only to simulate the real app where a component in the panel has the focus
      button.requestFocusInWindow();

      // If "A" or "B" key is pressed anywhere, MyAction.actionPerformed() will be called
      panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("A"), "MyAction");
      panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("B"), "MyAction");
      panel.getActionMap().put("MyAction", new MyAction());

      // Prepare JFrame
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      pack();
      setLocationRelativeTo(null);
   }

   private class MyAction extends AbstractAction {

      @Override
      public void actionPerformed(ActionEvent e) {

         System.out.println("EDT? " + SwingUtilities.isEventDispatchThread());  // Always prints TRUE

         if (dialog == null) {
            dialog = new MyDialog();
         }

         // Retrieve the key used to trigger the action
         char c = e.getActionCommand().charAt(0);

         // Prepare the dialog (insert the char)
         dialog.prepare(c);

         // Show dialog
         dialog.setVisible(true);
      }
   }

   private class MyDialog extends JDialog {

      JTextField textfield;

      /**
       * A simple dialog with just a textfield.
       */
      public MyDialog() {
         textfield = new JTextField("Hello");
         textfield.setColumns(100);
         getContentPane().add(textfield);
         pack();
         setLocationRelativeTo(null);
      }

      /**
       * Append the key (uppercased) at the end of the textfield
       */
      public void prepare(char c) {
         String text = textfield.getText();
         textfield.setText(text + " " + Character.toUpperCase(c));
      }

      /**
       * Overridden to add a global key binding on ESC key to exit the dialog.
       * <p>
       * This is only to facilitate the test where I need to try many times the process pressing "a" ESC "a" ESC etc.
       *
       * @return
       */
      @Override
      protected JRootPane createRootPane() {
         JRootPane contentPane = new JRootPane();

         contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("ESCAPE"), "actionCancel");
         contentPane.getActionMap().put("actionCancel", new AbstractAction("Cancel") {
            @Override
            public void actionPerformed(ActionEvent e) {
               setVisible(false);
            }
         });
         return contentPane;
      }
   }
}
/**
*尝试重现双键问题…失败,因为此操作正常!!:-(
*/
公共类PbKeyDouble扩展JFrame{
我的对话;
公共静态void main(字符串[]args){
javax.swing.SwingUtilities.invokeLater(新的Runnable(){
@凌驾
公开募捐{
PbKeyDouble o=新的PbKeyDouble();
o、 setVisible(真);
}
});
}
公共密钥双(){
//GUI初始化
//添加基本面板
JPanel面板=新的JPanel();
getContentPane().add(面板);
面板。设置首选尺寸(新尺寸(300200));
JButton按钮=新JButton(“按钮”);
面板。添加(按钮);
//按钮未使用,它仅用于模拟面板中某个组件具有焦点的真实应用程序
requestFocusInWindow()按钮;
//如果在任意位置按下“A”或“B”键,将调用MyAction.actionPerformed()
panel.getInputMap(JComponent.WHEN_FOCUSED_组件的祖先_).put(KeyStroke.getKeyStroke(“A”),“MyAction”);
panel.getInputMap(JComponent.WHEN_FOCUSED_组件的祖先_).put(KeyStroke.getKeyStroke(“B”),“MyAction”);
panel.getActionMap().put(“MyAction”,newmyAction());
//准备JFrame
setDefaultCloseOperation(关闭时退出);
包装();
setLocationRelativeTo(空);
}
私有类MyAction扩展了AbstractAction{
@凌驾
已执行的公共无效操作(操作事件e){
System.out.println(“EDT?”+SwingUtilities.isEventDispatchThread());//始终打印为TRUE
如果(对话框==null){
dialog=新建MyDialog();
}
//检索用于触发操作的键
char c=e.getActionCommand().charAt(0);
//准备对话框(插入字符)
对话.准备(c);
//显示对话框
对话框.setVisible(true);
}
}
私有类MyDialog扩展JDialog{
JTextField-textfield;
/**
*一个简单的对话框,只有一个文本字段。
*/
公共MyDialog(){
textfield=newjtextfield(“你好”);
textfield.setColumns(100);
getContentPane().add(文本字段);
包装();
setLocationRelativeTo(空);
}
/**
*将键(大写)追加到文本字段的末尾
*/
公共空间准备(字符c){
String text=textfield.getText();
textfield.setText(text+“”+Character.toUpperCase(c));
}
/**
*重写以在ESC键上添加全局键绑定以退出对话框。
*
*这只是为了方便测试,我需要尝试多次按下“a”ESC“a”ESC等。
*
*@返回
*/
@凌驾
受保护的JRootPane createRootPane(){
JRootPane contentPane=新的JRootPane();
contentPane.getInputMap(JComponent.WHEN\u FOCUSED\u组件的祖先\u).put(KeyStroke.getKeyStroke(“ESCAPE”),“actionCancel”);
contentPane.getActionMap().put(“actionCancel”),new AbstractAction(“Cancel”){
@凌驾
已执行的公共无效操作(操作事件e){
setVisible(假);
}
});
返回内容窗格;
}
}
}
但有时,可能是1/20次,我在JTextfield的末尾得到“Cc”

随机问题通常是线程问题的结果

所有Swing组件都应该在
事件调度线程(EDT)
上创建和修改

main()方法中的代码没有在EDT上执行,这可能是问题所在

创建GUI的代码应该包装在
SwingUtilities.invokeLater(…)


查看上的Swing教程以了解更多信息。

我发现了这个问题,尽管它对我来说仍然不符合逻辑。欢迎解释

所有Swing组件都应该在事件分派线程(EDT)上创建和修改

是的,在我的代码和sti中就是这样