如何通过文本窗格隐藏 Swing 加速器?

How to hide Swing accelerators by a text pane?

我有一个带有多个窗格的 Swing 应用程序。有些窗格是文本窗格 (JTextPane),有些是类似对话框的(带有按钮和滑块),有些是图形的(自定义绘制的)。我在主菜单中定义了一些带有简单快捷键的操作,例如 KPO

我希望仅当当前获得焦点的窗格未处理这些快捷键时,它们才由菜单操作处理。具体来说,当用户只是在文本窗格中键入内容时,我不希望它们被菜单处理。

我正在使用以下方法创建操作和菜单项:

action = new javax.swing.AbstractAction
new MenuItem(action)

我正在注册加速器:

action.putValue(javax.swing.Action.ACCELERATOR_KEY, keyStroke)

是否可以"eat"(抑制)在文本窗格中处理的按键的按键事件,以便它们不会传递到主菜单进行全局处理?

如果没有,是否有一些替代方法可以做类似的事情,比如注册我知道在某些窗格的文本窗格中不应处理的加速器?

我正在根据答案添加代码以使问题更清楚(并使开发替代解决方案更容易):

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;

public class TestMenuBindings {

    public static void main(String[] args) {
        JMenuBar menuBar = new JMenuBar();
        final JMenu menu = new JMenu("Print");
        final Action oAction = new PrintAction("O",KeyStroke.getKeyStroke(KeyEvent.VK_O, 0));
        menu.add(oAction);
        menuBar.add(menu);
        JFrame frm = new JFrame("Frame");
        frm.setJMenuBar(menuBar);
        JTextArea area = new JTextArea("Here I want no accelerators", 10, 40);
        frm.add(new JScrollPane(area));
        frm.add(new JTextField("Here I want accelerators working"), BorderLayout.SOUTH);
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
    }

    private static class PrintAction extends AbstractAction {
        private String str;
        public PrintAction(String aPrintStr, KeyStroke aMnemonic) {
            super("Print: " + aPrintStr);
            str = aPrintStr;
            putValue(Action.ACCELERATOR_KEY, aMnemonic);
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(str);
        }
    }
}

这是例子。在文本区域没有键绑定工作。在文本字段中工作所有键绑定。此外,所有菜单项都可以从菜单访问(启用)。

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;

public class TestMenuBindings {

    public static void main(String[] args) {
        JMenuBar menuBar = new JMenuBar();
        final JMenu menu = new JMenu("Print");
        menu.add(new PrintAction("O", KeyStroke.getKeyStroke(KeyEvent.VK_O, 0)));
        menu.add(new PrintAction("K", KeyStroke.getKeyStroke(KeyEvent.VK_K, 0)));
        menu.add(new PrintAction("P", KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)));
        menuBar.add(menu);
        JFrame frm = new JFrame("Frame");
        frm.setJMenuBar(menuBar);
        JTextArea area = new JTextArea("Here working no accelerators", 10, 40);
        area.addFocusListener(new FocusListener() {

            @Override
            public void focusLost(FocusEvent e) {
                setItemStatus(menu, true);
            }

            @Override
            public void focusGained(FocusEvent e) {
                setItemStatus(menu, false);
            }
        });
        frm.add(new JScrollPane(area));
        frm.add(new JTextField("Here working accelerators"), BorderLayout.SOUTH);
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
    }

    private static void setItemStatus(JMenu aMenu, boolean aStatus) {
        for (Component item : aMenu.getMenuComponents()) {
            ((JMenuItem) item).getAction().setEnabled(aStatus);
        }
    }
    private static class PrintAction extends AbstractAction {
        private String str;
        public PrintAction(String aPrintStr, KeyStroke aMnemonic) {
            super("Print: " + aPrintStr);
            str = aPrintStr;
            putValue(Action.ACCELERATOR_KEY, aMnemonic);
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(str);
        }
    }
}

这里有一个使用KeyBinds的解决方案,正如camickr. It is shorter than 所建议的,我觉得它更直接,但它有一个缺点,即快捷方式不显示在菜单中,这是快捷方式不显示的结果在操作本身中定义。

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;

public class TestMenuBindings {

    public static void main(String[] args) {
        JMenuBar menuBar = new JMenuBar();
        final JMenu menu = new JMenu("Print");
        final Action oAction = new PrintAction("O");
        menu.add(oAction);
        menuBar.add(menu);
        JFrame frm = new JFrame("Frame");
        frm.setJMenuBar(menuBar);
        JTextArea area = new JTextArea("Here working no accelerators", 10, 40);
        frm.add(new JScrollPane(area));
        frm.add(new JTextField("Here working accelerators") {
            {
                getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0), "command_O");
                getActionMap().put("command_O", oAction);
            }
        }, BorderLayout.SOUTH);
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
    }

    private static class PrintAction extends AbstractAction {
        private String str;
        public PrintAction(String aPrintStr) {
            super("Print: " + aPrintStr);
            str = aPrintStr;
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(str);
        }
    }
}

可以使用KeyEventDispatcher过滤按键事件。

(来源:我改编了 answer to Application wide keyboard shortcut 中的代码)

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class TestMenuBindings {

    public static void main(String[] args) {
        JMenuBar menuBar = new JMenuBar();
        final JMenu menu = new JMenu("Print");
        final Action oAction = new PrintAction("O",KeyStroke.getKeyStroke(KeyEvent.VK_O, 0));
        menu.add(oAction);
        menuBar.add(menu);
        JFrame frm = new JFrame("Frame");
        frm.setJMenuBar(menuBar);

        final JTextArea area = new JTextArea("Here working no accelerators", 10, 40);
        frm.add(new JScrollPane(area));

        frm.add(new JTextField("Here working accelerators"), BorderLayout.SOUTH);

        KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        kfm.addKeyEventDispatcher( new KeyEventDispatcher() {
            @Override
            public boolean dispatchKeyEvent(KeyEvent e) {
                KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e);
                // pass only KEY_TYPED for letters with no modifiers in the editing area, suppress KEY_PRESSED, KEY_RELEASED
                return area.isFocusOwner() && keyStroke.getModifiers()==0 && e.getID()!=KeyEvent.KEY_TYPED && Character.isLetter(e.getKeyChar());
            }
        });

        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
    }

    private static class PrintAction extends AbstractAction {
        private String str;
        public PrintAction(String aPrintStr, KeyStroke aMnemonic) {
            super("Print: " + aPrintStr);
            str = aPrintStr;
            putValue(Action.ACCELERATOR_KEY, aMnemonic);
        }
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(str);
        }
    }
}