在手动单击组件之前,焦点不起作用

Focus does not work until component is manually clicked

我正在尝试创建一个计算器,我正在使用 KeyBinders 来允许用户输入数字,而不必手动按下按钮。但是,我注意到在我用鼠标按下其中一个按钮之后,KeyBinders 才会工作。这是我的代码:

import java.awt.*;
...

public class Calculator extends JFrame {

    static JFrame f = new JFrame();
    static JPanel p = new JPanel();
    static JButton[] b = new JButton[19];
    static String[] bs = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
            ".", "+", "-", "*", "/", "=", "C", "\u2190", "\u00B1" };
    static JTextArea t = new JTextArea();

    public static void main(String[] args) {
        f.setSize(300, 300);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setResizable(false);

        for (int i = 0; i < b.length; i++) {

            b[i] = new JButton();
            b[i].setText(bs[i]);
            b[i].addActionListener(new BAction());
            b[i].setFont(new Font("Arial", Font.BOLD, 17));
            b[i].setFocusable(true);

        }

        setLayout(p, b);

        f.add(p);
        f.setVisible(true);

    }

    public static JPanel setLayout(JPanel p, JButton[] b) {
        Image icon = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE);
        f.setIconImage(icon);
        GridBagLayout g = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        p.setLayout(g);
        p.setSize(f.getSize());
        t.setEditable(false);
        t.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        t.setFont(new Font("Arial", Font.PLAIN, 15));
        t.setFocusable(false);
        t.setText("0");
        f.setFocusable(true);
        p.setFocusable(true);
        key();

        ...
        // GridBagLayouting here
        ...

        return p;

    }

    static class BAction implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            ...
            // Assigning ActionEventListeners
            ...

        }



    @SuppressWarnings({ "serial" })
    private static void key() {
        // 0
        Action b0 = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                b[0].doClick();
            }
        };

        p.getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), "b0");
        p.getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_0, 0), "b0");

        p.getActionMap().put("b0", b0);

        // 1
        Action b1 = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                b[1].doClick();
            }
        };

        p.getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD1, 0), "b1");
        p.getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_1, 0), "b1");

        p.getActionMap().put("b1", b1);

        // 2
        Action b2 = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                b[2].doClick();
            }
        };

        p.getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD2, 0), "b2");
        p.getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_2, 0), "b2");

        p.getActionMap().put("b2", b2);

        // +
        Action bplus = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                b[11].doClick();
            }
        };

        p.getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "b+");
        p.getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS,
                        InputEvent.SHIFT_MASK), "b+");

        p.getActionMap().put("b+", bplus);

    }

}

您可以在创建绑定时使用 WHEN_IN_FOCUSED_WINDOW 而不是 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT

更新

/**
 * Constant used for <code>registerKeyboardAction</code> that
 * means that the command should be invoked when the receiving
 * component is an ancestor of the focused component or is
 * itself the focused component.
 */
public static final int WHEN_ANCESTOR_OF_FOCUSED_COMPONENT = 1;

/**
 * Constant used for <code>registerKeyboardAction</code> that
 * means that the command should be invoked when
 * the receiving component is in the window that has the focus
 * or is itself the focused component.
 */
public static final int WHEN_IN_FOCUSED_WINDOW = 2;

I am using KeyBinders to allow the user to type out numbers as well instead of having to manually press the buttons

不要为每个键绑定创建唯一的操作。您可以轻松创建一个由所有号码共享的操作:

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

public class CalculatorPanel extends JPanel
{
    private JTextField display;

    public CalculatorPanel()
    {
        Action numberAction = new AbstractAction()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                display.setCaretPosition( display.getDocument().getLength() );
                display.replaceSelection(e.getActionCommand());
            }
        };

        setLayout( new BorderLayout() );

        display = new JTextField();
        display.setEditable( false );
        display.setHorizontalAlignment(JTextField.RIGHT);
        add(display, BorderLayout.NORTH);

        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout( new GridLayout(0, 5) );
        add(buttonPanel, BorderLayout.CENTER);

        for (int i = 0; i < 10; i++)
        {
            String text = String.valueOf(i);
            JButton button = new JButton( text );
            button.addActionListener( numberAction );
            button.setBorder( new LineBorder(Color.BLACK) );
            button.setPreferredSize( new Dimension(50, 50) );
            buttonPanel.add( button );

            InputMap inputMap = button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            inputMap.put(KeyStroke.getKeyStroke(text), text);
            inputMap.put(KeyStroke.getKeyStroke("NUMPAD" + text), text);
            button.getActionMap().put(text, numberAction);
        }
    }

    private static void createAndShowUI()
    {
//      UIManager.put("Button.margin", new Insets(10, 10, 10, 10) );

        JFrame frame = new JFrame("Calculator Panel");
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.add( new CalculatorPanel() );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible(true);
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}