如果基础窗口在ESC上关闭,如何为JPopupMenu启用ESC-Close?

问题描述 投票:2回答:2

想象两个常见的情况:一个在VK_ESCAPE(在根窗格上设置为键绑定)关闭的JDialog(或JFrame)和一个在ESC上也关闭的内部JPopupMenu。问题是:如果可以看到弹出窗口,则按Escape键总是会关闭对话框-事件。显然,弹出窗口甚至没有收到按键事件,因此弹出窗口无法使用它。有什么方法可以使它正常工作,以便在第一个ESC事件中关闭弹出窗口,而在第二个对话框中关闭对话框?顺便说一句:它与JComboBox一起使用,默认情况下,按下Escape键时,JComboBox会关闭。

java jdialog keyevent jpopupmenu
2个回答
5
投票

寻找通用解决方案有点挑战。我们需要考虑何时:

  1. 使用轻量级弹出窗口
  2. 使用重量级弹出窗口

我确定在两种情况下,按下逃逸键时,根窗格实际上都具有焦点。

在第一种情况下,我只是搜索根窗格以查看JPopupMenu是否已添加到GUI。如果是这样,那么我们可以关闭弹出窗口。

在第二种情况下,将创建一个包含JPopupMenu的窗口,因此我进行搜索以查看是否显示了可见的自定义弹出窗口。如果是这样,那么我将丢弃该窗口。

如果以上两种情况都不成立,则可以关闭对话框。

import java.awt.*;
import java.awt.event.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;

public class DialogEscape extends JDialog
{
    private JPopupMenu popup;

    public DialogEscape()
    {
        popup = new JPopupMenu();
        popup.add( new JMenuItem("SubMenuA") );
        popup.add( new JMenuItem("SubMenuB") );
        popup.add( new JMenuItem("SubMenuC") );
        popup.add( new JMenuItem("SubMenuD") );

        String[] items = { "Select Item", "Color", "Shape", "Fruit" };
        JComboBox comboBox = new JComboBox( items );
        add(comboBox, BorderLayout.NORTH);

        JTextField textField = new JTextField("Right Click For Popup");
        textField.setComponentPopupMenu(popup);
        add(textField);

        KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
        Action escapeAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                boolean openPopup = false;
                Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();

                //  Check if light weight popup is being used

                List<JPopupMenu> popups = SwingUtils.getDescendantsOfType(JPopupMenu.class, (Container)c, true);

                for (JPopupMenu p: popups)
                {
                    p.setVisible( false );
                    openPopup = true;
                }

                //  Check if a heavy weight popup is being used

                Window window = SwingUtilities.windowForComponent(c);
                Window[] windows = window.getOwnedWindows();

                for (Window w: windows)
                {
                    if (w.isVisible()
                    &&  w.getClass().getName().endsWith("HeavyWeightWindow"))
                    {
                        openPopup = true;
                        w.dispose();
                    }
                }

                //  No popups so close the Window

                if (! openPopup)
//                  SwingUtilities.windowForComponent(c).setVisible(false);
                    SwingUtilities.windowForComponent(c).dispose();
            }
        };

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE");
        getRootPane().getActionMap().put("ESCAPE", escapeAction);
    }

    public static void main(String[] args)
    {
        String laf = null;
        laf = "javax.swing.plaf.metal.MetalLookAndFeel";
//      laf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
//      laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";

        try { UIManager.setLookAndFeel(laf); }
        catch (Exception e2) { System.out.println(e2); }

        JDialog dialog = new DialogEscape();
        dialog.setDefaultCloseOperation( HIDE_ON_CLOSE );
        dialog.setSize(200, 200);
        dialog.setLocationRelativeTo(null);
        dialog.setVisible( true );
    }
}

您还需要下载Swing Utils类。


0
投票

我也遇到了问题。 @camickr提供的解决方案对我来说似乎不是很好。因此,我查看了到底发生了什么。打开弹出窗口时,作为弹出窗口的调用者的组件将失去焦点。焦点移至JDialog的JRootPane,然后将其委托给弹出窗口。因此,我没有在JRootPane上注册“ ESC”快捷方式,而是将其放在ContentPane上。这样,仅当聚焦JRootPane以外的其他内容时,对话框才会在ESC上关闭。

    final JComponent contentPane = (JComponent) dialog.getContentPane();
    contentPane.getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ).put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), "EXIT" );
    contentPane.getActionMap().put( "EXIT", new AbstractAction()
    {
      @Override
      public void actionPerformed( final ActionEvent e )
      {
        dialog.dispatchEvent( new WindowEvent( dialog, WindowEvent.WINDOW_CLOSING ) );
      }
    } );
© www.soinside.com 2019 - 2024. All rights reserved.