简体   繁体   中英

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

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. I also wanted the user to be presented with the same option if they also pressed the escape key (ESC), unfortunately it seems to be trapped in a state where the escape key seems to be constantly pressed when it is not. Where is the mistake and why?

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

Your design is incorrect.

You should NOT have a while (true) loop.

GUI's are event driven. You create the frame and make it visible. That is the end of the code in your main() method or constructor. The GUI will then sit there forever doing nothing.

However, eventually, the user will then generate events that the GUI responds to.

This means that the code to display the JOptionPane should be moved to the windowClosing() method of your WindowListener .

See: Closing an Application for some basics and helpful classes to use.

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

Don't use a KeyListener.

Swing was designed to be used with Key Bindings .

You can use the ExitAction contained in the Closing an Application link when creating your key bindings:

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

Read the Swing tutorial . There are section on :

  1. How to Uuse Key Bindings
  2. How to Use Actions

to help explain how the above suggestions work.

The tutorial examples will also show you how to better structure your code and note that the never use a while (true) loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM