【问题标题】:Why does Jtoolbar break my keyBindings?为什么 Jtoolbar 会破坏我的 keyBindings?
【发布时间】:2013-06-07 12:14:55
【问题描述】:

我一直在尝试将箭头键用作我的应用程序的一部分。遵循最佳实践,我坚持使用键绑定。我发现箭头键不会产生键类型事件,所以我使用了这个answer

但是,我的应用程序有许多组件,我发现如果我的 JFrame 中有一个 JToolbar,则上一个链接中的方法不再有效。为什么会这样?我如何拥有 JToolbar 并使用键绑定?

这是一个SSCCE

public class ArrowTest extends JFrame {

    public static void main(final String[] args){
        final ArrowTest at = new ArrowTest();
        at.setSize(100,200);

        final JPanel jp = new JPanel();
        jp.setBackground(Color.BLUE);
        at.getContentPane().add(jp);
        final JToolBar toolbar = new JToolBar();
        toolbar.add(new JButton());
        //at.add(toolbar);
        at.setVisible(true);
    }

    public ArrowTest() {
        super();    
        this.getContentPane().setLayout(new GridBagLayout());    
        this.getContentPane().setBackground(Color.BLACK);    
        this.setKeyBindings();
    }

    public void setKeyBindings() {

        final int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;    
        final ActionMap actionMap = this.getRootPane().getActionMap();
        final InputMap inputMap = this.getRootPane().getInputMap(condition);

        for (final Direction direction : Direction.values()) {
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V,0), direction.getText());
            inputMap.put(direction.getKeyStroke(), direction.getText());
            actionMap.put(direction.getText(), new MyArrowBinding(direction.getText()));
        }

    }

    enum Direction {
        UP("Up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0)),
        DOWN("Down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)),
        LEFT("Left", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)),
        RIGHT("Right", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));

        Direction(final String text, final KeyStroke keyStroke) {
            this.text = text;
            this.keyStroke = keyStroke;
        }
        private String text;
        private KeyStroke keyStroke;

        public String getText() {
            return text;
        }

        public KeyStroke getKeyStroke() {
            return keyStroke;
        }

        @Override
        public String toString() {
            return text;
        }
    }

    private class MyArrowBinding extends AbstractAction {

        private static final long serialVersionUID = -6904517741228319299L;

        public MyArrowBinding(final String text) {
            super(text);
            putValue(ACTION_COMMAND_KEY, text);
        }

        @Override
        public void actionPerformed(final ActionEvent e) {
            final String actionCommand = e.getActionCommand();
            System.out.println("Key Binding: " + actionCommand);
        }
    }    
}

【问题讨论】:

  • 你为你的框架设置了键绑定,你的工具栏似乎不在你的框架中

标签: java swing jframe jpanel key-bindings


【解决方案1】:

默认情况下,JToolBar 会为 KeyStroke 的 UP/DOWN/LEFT/RIGHT 注册一个动作,而您添加到 JFrameJToolBar 会自动获取焦点,因此在绑定到时您看不到任何内容根窗格(工具栏捕获您之前的事件)。

一种解决方案是使JToolBarJButton 不具有焦点,并允许您的JPanel 具有焦点(实际上您可以保留JToolBarJButton 并请求将焦点放在您的面板上,但是这也意味着您必须处理面板的焦点管理):

import java.awt.Color;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;

public class ArrowTest extends JFrame {

    public static void main(final String[] args) {
        final ArrowTest at = new ArrowTest();
        at.setSize(100, 200);

        final JPanel jp = new JPanel();
        jp.setBackground(Color.BLUE);
        jp.setFocusable(true);
        at.getContentPane().add(jp);
        final JToolBar toolbar = new JToolBar();
        JButton comp = new JButton();
        toolbar.add(comp);
        at.add(toolbar);
        at.setVisible(true);
    }

    public ArrowTest() {
        super();
        this.getContentPane().setLayout(new GridBagLayout());
        this.getContentPane().setBackground(Color.BLACK);
        this.setKeyBindings();
    }

    public void setKeyBindings() {
        for (final Direction direction : Direction.values()) {
            MyArrowBinding binding = new MyArrowBinding(direction.getText());
            getRootPane().registerKeyboardAction(binding, direction.getText(), direction.getKeyStroke(),
                    JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        }
    }

    enum Direction {
        UP("Up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0)), DOWN("Down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)), LEFT("Left",
                KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)), RIGHT("Right", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));

        Direction(final String text, final KeyStroke keyStroke) {
            this.text = text;
            this.keyStroke = keyStroke;
        }

        private String text;
        private KeyStroke keyStroke;

        public String getText() {
            return text;
        }

        public KeyStroke getKeyStroke() {
            return keyStroke;
        }

        @Override
        public String toString() {
            return text;
        }
    }

    private class MyArrowBinding extends AbstractAction {

        private static final long serialVersionUID = -6904517741228319299L;

        public MyArrowBinding(final String text) {
            super(text);
            putValue(ACTION_COMMAND_KEY, text);
        }

        @Override
        public void actionPerformed(final ActionEvent e) {
            final String actionCommand = e.getActionCommand();
            System.out.println("Key Binding: " + actionCommand);
        }
    }
}

我还将您对getActionMap/getInputMap 的调用替换为对javax.swing.JComponent.registerKeyboardAction(ActionListener, String, KeyStroke, int) 的一次调用

【讨论】:

  • 非常感谢您的回答。我将使用javax.swing.JComponent.registerKeyboardAction(ActionListener, String, KeyStroke, int)。查看您的解决方案,我似乎需要在添加到工具栏的每个组件上 setFocusable(false)。我认为最好将工具栏上的操作替换一次。
  • @medPhys-pl 实际上没有。您甚至可以让JToolbar 成为焦点,但您还需要以某种方式使您的组件具有焦点并处理鼠标单击以请求焦点。您的解决方案根本不理想,因为您取消了 JToolBar 的默认操作,并且如果其他一些组件(不在 JToolBar 下)抓住焦点,您的操作将不再起作用。
  • 我已经包含了我的解决方案的更多代码。我认为这表明您对另一个组件的第二点关注焦点不会成为问题。从我之前的帖子中不清楚。你是对的,我确实杀死了默认操作。但是覆盖默认操作有什么问题吗?
  • @medPhys-pl 我通常会尽量避免这种情况,原因有两个:1)如果要在运行时更改 L&F,您的修复程序将被终止。 2) 默认的键绑定/动作通常会起作用,因此杀死它们对于某些用户来说可能看起来很奇怪。然而,如果 API 在那里,没有什么能阻止你这样做。我的意见是尽量避免这种代码,因为它闻起来不好。
  • @medPhys-pl“外观和感觉”。它定义了组件的外观(Windows 上的进度条与 Mac 上的进度条非常不同)。它还定义了组件“感觉/工作/行为”的方式。例如,在 Windows 上,您使用右键单击来显示弹出菜单。在 Mac 上,您使用 CTRL+左键单击。在 Windows 上,您将使用 CTRL+C 进行复制,而在 Mac 上,您将使用“Apple”+C。所有这些差异都由 L&F 处理。尝试一次添加此行(作为main() 的第一行):try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch(Exception e) { e.printStackTrace(); }
【解决方案2】:

按照answer 中的建议,我已经使用

解决了这个问题
public void setKeyBindings() {

    final int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;    
    final ActionMap actionMap = this.getRootPane().getActionMap();
    final InputMap inputMap = this.getRootPane().getInputMap(condition);

    for (final Direction direction : Direction.values()) {
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V,0), direction.getText());
        inputMap.put(direction.getKeyStroke(), direction.getText());
        actionMap.put(direction.getText(), new MyArrowBinding(direction.getText()));
    }

    condition = JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT;

    actionMap = toolbar.getActionMap();
    inputMap = toolbar.getInputMap(condition);


    for (final Direction direction : Direction.values()) {
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V,0), direction.getText());
        inputMap.put(direction.getKeyStroke(), direction.getText());
        actionMap.put(direction.getText(), new MyArrowBinding(direction.getText()));
    } 


}

【讨论】:

    猜你喜欢
    • 2013-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-25
    • 2011-08-19
    相关资源
    最近更新 更多