java - 制作帮助菜单

标签 java swing inner-classes keylistener

我正在向我的程序添加一个帮助菜单,并且我试图找出是否有一种方法可以拦截键盘输入,而无需在 JFrame 上使用 setFocusable 方法。如果我将焦点设置到框架上并按 F1,则没有问题。当他们按下任何其他组件时,问题就会出现。框架不再处于焦点状态,因此 F1 键不再显示菜单。这是我的做法

package helpmenu;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;

public class HelpMenu extends JFrame
{
    public HelpMenu() 
    {
        setSize(300, 300);
        setTitle("Help Menu");
    }


    /**
     * Sets a KeyListener to
     * the passed in JFrame.
     * @param mainFrame
     */
    public void callMenu( JFrame mainFrame )
    {
        mainFrame.addKeyListener( new functionListener() ); //Set keylistener to the main frame.
        mainFrame.setFocusable(true); //Put the main frame into focus.
    }

    /**
     * Inner KeyListener
     * class for the mainFrame
     * @author Shaun
     */
    class functionListener implements KeyListener
    {
        @Override
        public void keyPressed(KeyEvent e)
        {
            //Check if the F1 key was pressed
            if ( e.getKeyCode() == KeyEvent.VK_F1 ) { 
                setVisible(true);
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {}

        @Override
        public void keyTyped(KeyEvent e) {}
    }
}

最佳答案

不要将 KeyListener 用于此类事情。正如您所注意到的,原因之一是它要求组件处于焦点位置。相反,use a Key Binding使用 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT 输入 map 。

此外,有些相关,请参阅 "The Use of Multiple JFrames, Good/Bad Practice?"您的帮助菜单可能应该是一个 JDialog。

这是使用键绑定(bind)的示例:

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

class HelpDialogEx {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new HelpDialogEx();
            }
        });
    }

    final JFrame frame = new JFrame("Press F1 for help");
    final JPanel cpane = new JPanel(new GridLayout(4, 5, 10, 10));

    final HelpDialog help = new HelpDialog(frame);

    final AbstractAction helpToggle = new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            help.setVisible(!help.isVisible());
        }
    };

    HelpDialogEx() {
        for(int i = 0; i < 20; i++) {
            cpane.add(new JButton("Nil"));
        }

        cpane.setPreferredSize(new Dimension(640, 480));
        frame.setContentPane(cpane);

        addHelpToggle(frame.getRootPane());
        addHelpToggle(help.getRootPane());

        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    void addHelpToggle(JComponent comp) {
        KeyStroke f1Key = KeyStroke.getKeyStroke(
            KeyEvent.VK_F1, 0, true
        );
        String cmd = "helpToggle";

        for(InputMap im : new InputMap[] {
            comp.getInputMap(JComponent.WHEN_FOCUSED),
            comp.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
        }) {
            im.put(f1Key, cmd);
        }

        comp.getActionMap().put(cmd, helpToggle);
    }

    static class HelpDialog
    extends JDialog {
        HelpDialog(Window parent) {
            super(parent, "\"Help\"", Dialog.ModalityType.MODELESS);

            JTextArea doc = new JTextArea(getUnhelpfulText());
            doc.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
            doc.setEditable(false);
            doc.setLineWrap(true);

            JScrollPane scroll = new JScrollPane(doc);
            scroll.getViewport().setPreferredSize(new Dimension(480, 360));

            setContentPane(scroll);

            pack();
            setLocationRelativeTo(null);
        }

        static String getUnhelpfulText() {
            StringBuilder sb = new StringBuilder();

            addRandomParagraph(sb);
            for(int i = 0; i < 3; i++) {
                sb.append("\n\n");
                addRandomParagraph(sb);
            }

            return sb.toString();
        }

        static void addRandomParagraph(StringBuilder sb) {
            sb.append("     ");
            addRandomSentence(sb);
            for(int i = 0; i < 10; i++) {
                sb.append(' ');
                addRandomSentence(sb);
            }
        }

        static void addRandomSentence(StringBuilder sb) {
            sb.append((char)(Math.random() * 26 + 'A'));

            for(int i = (int)(Math.random() * 10 + 1); i > 0; i--) {
                for(int len = (int)(Math.random() * 10 + 1); len > 0; len--) {
                    sb.append((char)(Math.random() * 26 + 'a'));
                }

                sb.append(' ');
            }

            sb.setCharAt(sb.length() - 1, '.');
        }
    }
}

我发现虽然教程说 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT 应该涵盖组件本身聚焦的时间,但事实并非如此。

关于java - 制作帮助菜单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21685029/

相关文章:

java - Java 有静态顺序初始化失败吗?

java - 这个包含内部类的变量到底做什么?

java - 如何不通过查询字符串更改 I18N 语言环境?

java - 关闭 BufferedWriter 会删除写入文件的所有文件数据吗?

java - 如何在面板的其余组件上绘制停靠的 JToolBar

java - 切换 JPanels 和 keyListeners

java - 如何让 Swing JButton 在按下时重复它的 Action ?

c# - 私有(private)内部类上的动态失败

android - 带有自定义对象列表的可打包类

java - 如何将我的代码从 applet 更改为 JFrame?