java - 禁用单个 ALT 类型来激活菜单

标签 java swing shortcut look-and-feel jmenu

当 JTextArea 获得焦点并且框架安装了 JMenu 时,我尝试禁用 Windows 外观中 ALT 键的默认行为。默认情况下,菜单会获得键盘焦点,因此您无法继续在那里输入内容。

我的第一次尝试是通过将键添加到 JTextArea InputMap 来捕获单次 ALT 按下:

//Disable ALT key presses when the textarea is focused
jta.getInputMap(javax.swing.JComponent.WHEN_FOCUSED).put(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ALT, 0, true), "ALTKEY");
jta.getActionMap().put("ALTKEY", new javax.swing.AbstractAction() {
    private static final long serialVersionUID = 1L;
    @Override
    public void actionPerformed(java.awt.event.ActionEvent e) {
        System.out.println("Performed");
    }
});

我注意到这在某些第三方虚拟机中按预期工作,但在最新的 Oracle 1.8.0_211 中却不起作用

然后我尝试使用 AWT 组件中的关键监听器并在事件到达其他组件之前使用该事件。

//Disable ALT key presses when the textarea is focused, second attempt
jta.addKeyListener(new java.awt.event.KeyListener(){
    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
            System.out.println("Pressed");
            e.consume();
        }
    }
    @Override
    public void keyReleased(KeyEvent e) {
        if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
            System.out.println("Released");
            e.consume();
        }
    }
    @Override
    public void keyTyped(KeyEvent e) {
        if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
            System.out.println("Typed");
            e.consume();
        }
    }
});

运气不好。

最后,我尝试更改用于创建菜单快捷方式的键并完全摆脱令人讨厌的 ALT 行为:

//Disable the ALT for the Mnemonic and put CTRL:
int[] ints = {java.awt.event.KeyEvent.CTRL_MASK};
javax.swing.UIManager.put("Menu.shortcutKeys", ints);

这避免了使用 ALT 键的快捷方式(并且 CTRL 完美地工作),但并没有阻止按一次 ALT 键时的标准行为。

将 L&F 更改为其他平台,或者不将其设置为 Windows 平台可以解决问题,但当然,它会改变整个程序的外观。

我想我做错了什么,但是更改InputMap或添加KeyListeners对第三方虚拟机有效,所以我开始认为这可能是一个错误。

关于如何查找谁在监听 ALT 按键并避免它,有什么想法吗? 有没有办法自定义 L&F 或查看其配置是什么?

以下是测试的完整代码:

public class JMenubarAltTest extends javax.swing.JFrame{
    private static final long serialVersionUID = 1L;

    public JMenubarAltTest(){
        super();

        //Disable the ALT for the Mnemonic and put CTRL key instead:
        int[] ints = {java.awt.event.KeyEvent.CTRL_MASK};
        javax.swing.UIManager.put("Menu.shortcutKeys", ints);

        //Put the platform L&F, in my case I need this to work in Windows
        try {
            javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException e1) {
            e1.printStackTrace();
        }

        this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

        //Create a random menu with some random items
        javax.swing.JMenuBar jmb = new javax.swing.JMenuBar();
        javax.swing.JMenu jm1 = new javax.swing.JMenu("menu1");
        jm1.setMnemonic(java.awt.event.KeyEvent.VK_M);
        jm1.add(new javax.swing.JMenuItem("item1"));
        jm1.add(new javax.swing.JMenuItem("item2"));
        jm1.add(new javax.swing.JMenuItem("item3"));

        javax.swing.JMenu jm2 = new javax.swing.JMenu("menu1");
        jm2.setMnemonic(java.awt.event.KeyEvent.VK_E);
        jm2.add(new javax.swing.JMenuItem("item1"));
        jm2.add(new javax.swing.JMenuItem("item2"));
        jm2.add(new javax.swing.JMenuItem("item3"));

        jmb.add(jm1);
        jmb.add(jm2);
        this.setJMenuBar(jmb);

        //Get a JTextArea to show what happens with the focus
        javax.swing.JTextArea jta = new javax.swing.JTextArea();

        //Disable ALT key presses when the textarea is focused
        jta.getInputMap(javax.swing.JComponent.WHEN_FOCUSED).put(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ALT, 0, true), "ALTKEY");
        jta.getActionMap().put("ALTKEY", new javax.swing.AbstractAction() {
            private static final long serialVersionUID = 1L;
            @Override
            public void actionPerformed(java.awt.event.ActionEvent e) {
                System.out.println("Performed");
            }
        });

        //Disable ALT key presses when the textarea is focused, second attempt
        jta.addKeyListener(new java.awt.event.KeyListener(){
            @Override
            public void keyPressed(java.awt.event.KeyEvent e) {
                if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
                    System.out.println("Pressed");
                    e.consume();
                }
            }
            @Override
            public void keyReleased(java.awt.event.KeyEvent e) {
                if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
                    System.out.println("Released");
                    e.consume();
                }
            }
            @Override
            public void keyTyped(java.awt.event.KeyEvent e) {
                if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
                    System.out.println("Typed");
                    e.consume();
                }
            }
        });

        this.getContentPane().add(jta);
        this.pack();
        this.setSize(200, 200);
        this.setVisible(true);
    }

    public static void main(java.lang.String[] args){
        new JMenubarAltTest();
    }
}

最佳答案

不幸的是,Windows L&F 中的替代处理器由于某些原因忽略了已消耗标志。所以我们需要使用反射 API 来删除它。

import java.awt.BorderLayout;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.lang.reflect.Method;
import java.util.List;

import javax.swing.JScrollPane;
import javax.swing.JTextField;

public class JMenubarAltTest extends javax.swing.JFrame {
    private static final long serialVersionUID = 1L;

    private KeyEventPostProcessor altPostprocessor;

    public JMenubarAltTest() {
        super();

        // Disable the ALT for the Mnemonic and put CTRL key instead:
        int[] ints = {java.awt.event.KeyEvent.CTRL_MASK};
        javax.swing.UIManager.put("Menu.shortcutKeys", ints);

        // Put the platform L&F, in my case I need this to work in Windows
        try {
            javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                | javax.swing.UnsupportedLookAndFeelException e1) {
            e1.printStackTrace();
        }

        this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

        // Create a random menu with some random items
        javax.swing.JMenuBar jmb = new javax.swing.JMenuBar();
        javax.swing.JMenu jm1 = new javax.swing.JMenu("menu1");
        jm1.setMnemonic(java.awt.event.KeyEvent.VK_M);
        jm1.add(new javax.swing.JMenuItem("item1"));
        jm1.add(new javax.swing.JMenuItem("item2"));
        jm1.add(new javax.swing.JMenuItem("item3"));

        javax.swing.JMenu jm2 = new javax.swing.JMenu("menu1");
        jm2.setMnemonic(java.awt.event.KeyEvent.VK_E);
        jm2.add(new javax.swing.JMenuItem("item1"));
        jm2.add(new javax.swing.JMenuItem("item2"));
        jm2.add(new javax.swing.JMenuItem("item3"));

        jmb.add(jm1);
        jmb.add(jm2);
        this.setJMenuBar(jmb);

        // Get a JTextArea to show what happens with the focus
        javax.swing.JTextArea jta = new javax.swing.JTextArea();

        // Disable ALT key presses when the textarea is focused
        jta.addFocusListener(new FocusListener() {

            @Override
            public void focusLost(FocusEvent e) {
                addAltPostprocessor();
            }

            @Override
            public void focusGained(FocusEvent e) {
                removeAltProcessor();
            }
        });

        this.getContentPane().add(new JScrollPane(jta));
        this.getContentPane().add(new JTextField(), BorderLayout.SOUTH);
        this.setSize(400, 300);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }

    // use reflection to remove the Windows L&F ALT processor.
    private void removeAltProcessor() {
        if (altPostprocessor == null) {
            try {
                Method method = KeyboardFocusManager.class.getDeclaredMethod("getKeyEventPostProcessors");
                method.setAccessible(true);
                @SuppressWarnings("unchecked")
                List<KeyEventPostProcessor> list =
                        (List<KeyEventPostProcessor>) method.invoke(KeyboardFocusManager.getCurrentKeyboardFocusManager());
                for (KeyEventPostProcessor pp : list) {
                    if (pp.getClass().getName().contains("WindowsRootPaneUI")) {
                        KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(pp);
                        altPostprocessor = pp;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(altPostprocessor);
        }
    }

    private void addAltPostprocessor() {
        if (altPostprocessor != null) {
            KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(altPostprocessor);
        }
    }

    public static void main(java.lang.String[] args) {
        new JMenubarAltTest();
    }
}

在此示例中,当文本区域获得焦点时,ALT 处理被禁用。对于文本字段,alt 处理仍然启用。

关于java - 禁用单个 ALT 类型来激活菜单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56339708/

相关文章:

java - 如何在高分辨率屏幕上缩放字体?

java - Java中的Windows快捷方式(.lnk)解析器?

c# - 在 .NET 应用程序中禁用辅助功能快捷方式?

java - Process.waitFor() 不等待执行的快捷方式 (.lnk)

java - 如何获取/发布多模块 Maven 项目站点到文件夹中

java - 重复插入时映射抛出错误

java - 是否可以通过 API 将一个复杂对象的 JSON 数据解析为自定义对象

java - CCCallFunc 中 cocos2d-android 中的 NoSuchMethodException

java - 将逻辑集成到paintComponent中

java - Apache POI - CellDataFormat 异常