如何覆盖JFrame关闭(' x')按钮的默认侦听器?

时间:2014-09-20 19:02:12

标签: java swing exit windowlistener

我想要3种退出应用程序的方法

  1. 按键CTRL-Q
  2. 选择'退出'来自菜单栏
  3. 关闭' x' JFrame上的按钮
  4. 我到目前为止所做的是为前两个添加偶数听众,但我无法弄清楚如何让JFrame关闭' x'按钮做同样的事情。现在它只是在不提示确认的情况下退出应用程序,因为它不知道如何实现。在用户确认要退出之后,我基本上希望处理所有帧。在前两种情况下会发生这种情况,原因很简单,因为它们的动作侦听器调用确认退出的exit()方法,然后继续处理所有帧。

    public class MainWindow extends JFrame {
        ...
        this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
    
        this.addWindowListener(new WindowListener() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        }
    
        ...
    
        // this is part of the main menu bar (case #2)
        JMenuItem mntmExit = new JMenuItem("Exit");
        mntmExit.addActionListener(new ActionListener() {
    
            @Override
            public void actionPerformed(ActionEvent e) {
                MainWindow.this.exit();
            }
    
        });
    
        // this is case #1
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
    
            @Override
            public boolean dispatchKeyEvent(KeyEvent e) {
                if(e.getKeyCode() == KeyEvent.VK_Q && e.getModifiers() == InputEvent.CTRL_MASK) {
                    MainWindow.this.exit();
                }
    
                return false;
            }
    
        });
    }
    
    // the exit method
    private void exit() {
        int confirmed = JOptionPane.showConfirmDialog(this, "Are you sure you want to quit?", "Confirm quit", JOptionPane.YES_NO_OPTION);
    
        if(confirmed == JOptionPane.YES_OPTION) {
            Frame[] frames = Frame.getFrames();
    
            for(Frame frame : frames) {
                frame.dispose();
            }
        }
    }
    

    是否可以为关闭按钮分配动作侦听器?如果没有,我还有另一种方法吗?

2 个答案:

答案 0 :(得分:4)

将JFrame的默认关闭操作保持为JFrame.DO_NOTHING_ON_CLOSE,但使用WindowListener(或更简洁的WindowAdapter)监听关闭尝试。

您可以对菜单项和按钮使用相同的AbstractAction,然后在WindowListener中调用action的方法。例如

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.*;

public class ClosingJFrame {
   public static void main(String[] args) {
      final JFrame frame = new JFrame("My Frame");
      frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

      final CloseAction closeAction = new CloseAction(frame);

      JPanel panel = new JPanel();
      panel.add(new JButton(closeAction));

      JMenuItem exitMenuItem = new JMenuItem(closeAction);
      JMenu menu = new JMenu("File");
      menu.setMnemonic(KeyEvent.VK_F);
      menu.add(exitMenuItem);
      JMenuBar menuBar = new JMenuBar();
      menuBar.add(menu);

      frame.setJMenuBar(menuBar);

      frame.addWindowListener(new WindowAdapter() {
         @Override
         public void windowClosing(WindowEvent e) {
            closeAction.confirmClosing();
         }
      });

      frame.add(panel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);


   }
}

class CloseAction extends AbstractAction {
   private JFrame mainFrame;

   public CloseAction(JFrame mainFrame) {
      super("Exit");
      putValue(MNEMONIC_KEY, KeyEvent.VK_X);
      this.mainFrame = mainFrame;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      confirmClosing();
   }

   public void confirmClosing() {
      int confirmed = JOptionPane.showConfirmDialog(mainFrame,
            "Are you sure you want to quit?", "Confirm quit",
            JOptionPane.YES_NO_OPTION);
      if (confirmed == JOptionPane.YES_OPTION) {
         // clean up code
         System.exit(0);
      }
   }
}

修改
糟糕,我忘记了你的ctrl-Q击键位。对此使用相同的Action,并使用KeyBindings将其绑定到ctrl-q键。改进的代码:

import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.*;

public class ClosingJFrame {
   public static void main(String[] args) {
      final JFrame frame = new JFrame("My Frame");
      frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

      final CloseAction closeAction = new CloseAction(frame);

      JPanel panel = new JPanel();
      panel.add(new JButton(closeAction));

      JMenuItem exitMenuItem = new JMenuItem(closeAction);
      JMenu menu = new JMenu("File");
      menu.setMnemonic(KeyEvent.VK_F);
      menu.add(exitMenuItem);
      JMenuBar menuBar = new JMenuBar();
      menuBar.add(menu);

      frame.setJMenuBar(menuBar);

      frame.addWindowListener(new WindowAdapter() {
         @Override
         public void windowClosing(WindowEvent e) {
            closeAction.confirmClosing();
         }
      });

      // also use the same Action in your ctrl-q key bindings
      int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
      InputMap inputMap = panel.getInputMap(condition);
      ActionMap actionMap = panel.getActionMap();
      KeyStroke ctrlQKey = KeyStroke.getKeyStroke(KeyEvent.VK_Q, InputEvent.CTRL_DOWN_MASK);
      inputMap.put(ctrlQKey, ctrlQKey.toString());
      actionMap.put(ctrlQKey.toString(), closeAction);

      frame.add(panel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);


   }
}

class CloseAction extends AbstractAction {
   private JFrame mainFrame;

   public CloseAction(JFrame mainFrame) {
      super("Exit");
      putValue(MNEMONIC_KEY, KeyEvent.VK_X);
      this.mainFrame = mainFrame;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      confirmClosing();
   }

   public void confirmClosing() {
      int confirmed = JOptionPane.showConfirmDialog(mainFrame,
            "Are you sure you want to quit?", "Confirm quit",
            JOptionPane.YES_NO_OPTION);
      if (confirmed == JOptionPane.YES_OPTION) {
         // clean up code
         System.exit(0);
      }
   }
}

编辑2
这句话让我担心:

  

然后继续处理所有帧

因为它意味着你有多个JFrame。如果是这样,您应该阅读此链接,因为它将解释为什么通常不需要这样做:The Use of Multiple JFrames, Good/Bad Practice?


编辑3
根据Rob Camick的评论:

  

您也可以通过执行以下操作来为CloseAction设置加速器:

putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control Q")); 
  

然后菜单项将为您执行键绑定。

这将进入CloseAction的构造函数,如下所示:

   public CloseAction(JFrame mainFrame) {
      super("Exit");
      putValue(MNEMONIC_KEY, KeyEvent.VK_X);
      putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control Q")); 
      this.mainFrame = mainFrame;
   }

答案 1 :(得分:2)

windowClosing方法中,请在exit()之前调用System.exit()方法。

单击X时会自动关闭java程序。