Java:总是在 JFrame 上的 WINDOW_CLOSING 的 dispatchEvent() 之后按下转义键

Java: The escape key is always pressed after dispatchEvent() for WINDOW_CLOSING on a JFrame

我有一个简单的 JFrame,它要求用户在单击 X 关闭 window 时确认退出,这工作正常。我还希望用户在按下转义键 (ESC) 时也能看到相同的选项,不幸的是,它似乎陷入了一种状态,即转义键似乎一直被按下,而实际上却没有。哪里错了,为什么?

public class Zz extends javax.swing.JFrame implements Events {
    boolean exitAttempt = false;
    java.awt.event.WindowEvent closeEvent;
    //public Zz() {}
    public static void main(java.lang.String[] args) {
        Zz zz = new Zz();
        zz.dostuff();
    }
    public void dostuff() {
        setSize(800, 600);
        setLocationRelativeTo(null);
        setResizable(false);
        setDefaultCloseOperation(javax.swing.JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent we) {
                exitAttempt = true;
            }
        });
        closeEvent = new java.awt.event.WindowEvent(
            this, java.awt.event.WindowEvent.WINDOW_CLOSING);
        setVisible(true);
        java.awt.Canvas canvas = new java.awt.Canvas();
        canvas.setPreferredSize(new java.awt.Dimension(800, 600));
        add(canvas);
        Keys keys = new Keys();
        addKeyListener(keys);
        pack();
        while (true) {
            events(keys);
            if (exitAttempt) {
                if (javax.swing.JOptionPane.YES_OPTION ==
                    showConfirmDialog("Do you want to Exit ?",
                        "Confirmation:", javax.swing.JOptionPane.YES_NO_OPTION,
                            javax.swing.JOptionPane.QUESTION_MESSAGE, null)) {
                    exit();
                    break; //while loop
                }
                exitAttempt = false;
            }
        }
        dispose();
    }
    public void triggerCloseEvent() {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                dispatchEvent(closeEvent);
            }
        });
    }
    public int showConfirmDialog(java.lang.Object message,
            java.lang.String title, int optionType, int messageType,
            javax.swing.Icon icon) {
        return javax.swing.JOptionPane.showConfirmDialog(
            this, message, title, optionType, messageType, icon);
    }
    public boolean exit() {
        setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
        return true;
    }
    public void events(Keys keys) {
System.out.println((keys.getPressed())[java.awt.event.KeyEvent.VK_ESCAPE]);
        if ((keys.getPressed())[java.awt.event.KeyEvent.VK_ESCAPE]) {
            triggerCloseEvent();
        }
    }
}
interface Events {
    public void events(Keys keys);
}
class Keys implements java.awt.event.KeyListener {
    private final boolean[] pressed;
    public Keys() {
        pressed = new boolean[256];
    }
    public void keyTyped(java.awt.event.KeyEvent event) {}
    public void keyPressed(java.awt.event.KeyEvent event) {
        pressed[event.getKeyCode()] = true;
    }
    public void keyReleased(java.awt.event.KeyEvent event) {
        pressed[event.getKeyCode()] = false;
    }
    public boolean[] getPressed() {
        return pressed;
    }
}

I have a simple JFrame that asks a user for a confirmation to exit when they click X to close the window, this works fine

您的设计不正确。

你不应该有一个 while (true) 循环。

GUI 是事件驱动的。您创建框架并使其可见。这是 main() 方法或构造函数中代码的结尾。然后 GUI 将永远坐在那里什么都不做。

但是,最终用户将生成 GUI 响应的事件。

这意味着显示 JOptionPane 的代码应该移动到 WindowListenerwindowClosing() 方法。

请参阅:Closing an Application 了解一些基础知识和有用的 类 使用方法。

I also wanted the user to be presented with the same option if they also pressed the escape key

不要使用 KeyListener。

Swing 设计用于 Key Bindings

您可以在创建键绑定时使用 Closing an Application link 中包含的 ExitAction

KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
InputMap im = frame.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(escapeKeyStroke, "escape");
frame.getRootPane().getActionMap().put("escape", new ExitAction());

阅读Swing tutorial。有关于 :

的部分
  1. 如何使用键绑定
  2. 如何使用动作

帮助解释上述建议的工作原理。

教程示例还将向您展示如何更好地构建代码并注意永远不要使用 while (true) 循环。