击键时关闭 JPopupMenu

Close JPopupMenu on Keystroke

我正在创建一个自动完成处理文本的工具 IDE,Java 的自定义版本。下面的class是弹出来呈现不同填写可能性的弹出菜单:

class Entry extends JPopupMenu implements KeyListener{

    private JEditTextArea parent;
    
    public Entry(String word, JEditTextArea _parent) {
        super();
        parent = _parent;
        parent.add(this);
        
        parent.addKeyListener(this);
        
        add(word);
    }
    
    public void show(int x, int y) {
        super.show(parent, x, y);
    }
    
    public void keyPressed(KeyEvent key) {
        setVisible(false);
        setEnabled(false);
    }
    
    public void keyReleased(KeyEvent key) {}

    public void keyTyped(KeyEvent key) {}
}

父项是 JEditTextArea,afaik 与 swing 包中的 JTextArea 共享其大部分功能。单词参数只是开始输入的单词,作为稍后在代码中查找合适补全的模式。

问题是,一旦弹出窗口打开,它就会阻止进一步输入新字符,您必须按 ESC 手动关闭它。我试图让它成为一个 KeyListener 并让它在击键时关闭,但到目前为止没有成功。我还尝试让它在任何击键时打印一条简单消息,但无论我尝试什么,我都无法说服它对任何形式的键输入做出反应。有什么方法可以让它在输入的击键时关闭吗?

~奥卡加纳

我无法真正使用您提供的 class,因为缺少一些 class。所以我刚刚创建了这个最小的工作示例,我希望它能说明你的问题:

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

public class Main {
    public static void main(String[] args) {
        //Creating elements
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        JButton button = new JButton("click me!");
        JPopupMenu menu = new JPopupMenu("Menu");
        menu.add("A");
        menu.add("B");
        menu.add("C");  

        //Open menu on button-click
        button.addActionListener( new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                menu.show(button, button.getWidth()/2, button.getHeight()/2);
            }
        });

        //Close menu on CTRL+X
        frame.addKeyListener(new KeyListener(){
            @Override
            public void keyPressed(KeyEvent e) {
                //See https://docs.oracle.com/javase/8/docs/api/javax/swing/JPopupMenu.html#setVisible-boolean-
                //See https://docs.oracle.com/javase/8/docs/api/javax/swing/JPopupMenu.html#isVisible--
                if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_X && menu.isVisible()) {
                    menu.setVisible(false);
                }
            }

            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
            }
        });

        //Putting everything together
        panel.add(button);
        frame.add(panel);
        frame.setSize(300,300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       
        frame.setVisible(true);
    }
}

这不起作用,因为密钥侦听器已添加到 JFrame。当按下按钮并打开菜单时,菜单获得焦点,因此 keylistener 不起作用。

这个问题可以通过在代码中添加以下几行来解决:

//this is doing the trick
button.setFocusable(false);
menu.setFocusable(false);

如果您不想这样做,只想将 KeyListener 添加到 JPopupMenu,那么您可以这样做:

//Open menu on button-click
button.addActionListener( new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        menu.show(button, button.getWidth()/2, button.getHeight()/2);
        menu.requestFocus();
    }
});

//Close menu on CTRL+X
menu.addKeyListener(new KeyListener(){
    @Override
    public void keyPressed(KeyEvent e) {
        //See https://docs.oracle.com/javase/8/docs/api/javax/swing/JPopupMenu.html#setVisible-boolean-
        //See https://docs.oracle.com/javase/8/docs/api/javax/swing/JPopupMenu.html#isVisible--
        if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_X && menu.isVisible()) {
            menu.setVisible(false);
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
});

这里的技巧是在菜单打开后将焦点设置在菜单上(在按钮的 actionlistener 中):

menu.requestFocus();