【问题标题】:How can I override the default listener for a JFrame close ('x') button?如何覆盖 JFrame 关闭 ('x') 按钮的默认侦听器?
【发布时间】:2014-09-20 19:02:11
【问题描述】:

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

  1. 按键 CTRL-Q
  2. 从菜单栏中选择“退出”
  3. 关闭 JFrame 上的“x”按钮

到目前为止,我所做的是为前两个添加偶数侦听器,但我无法弄清楚如何让 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();
        }
    }
}

是否可以为关闭按钮分配一个动作监听器?如果没有,我还有其他方法可以解决这个问题吗?

【问题讨论】:

  • 请参阅Closing an Application 以获得一个简单的界面,因为您只需要编写一个自定义操作,就可以更轻松地实现这些要求。

标签: java swing exit windowlistener


【解决方案1】:

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

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

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 按键位。为此使用相同的操作,并使用 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。您也可以通过执行以下操作为 CloseAction 设置加速器:putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control Q"));。然后菜单项将为您完成键绑定。
【解决方案2】:

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

当你点击 X 时,它会自动关闭你的 java 程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-29
    • 2011-09-06
    • 2013-09-30
    • 1970-01-01
    • 2011-03-01
    • 2012-12-17
    • 2011-11-06
    • 1970-01-01
    相关资源
    最近更新 更多