自 Java 1.7 以来的助记符行为(仍然存在于 1.8.0.121 中)



我们有一个在Java 1.6上运行的遗留软件。当我们最终获得将其升级到Java 1.8的绿灯时,以下问题出现了。

我们有一组带有加速键的单选按钮。如果任何类型的 JTextComponent 具有焦点,并且您点击其中一个单选按钮加速器(例如 ALT-s),并且在释放 ALT 之前释放"s",则 UIManager 将激活菜单栏。(这只发生在 Windows 外观和感觉上)

看起来像一个错误,我一直在考虑通过在这些情况下"消费"ALT 版本来编写解决方法,但也许有人有更好的主意?使用不同的外观不是一种选择,也不能关闭 UI 管理器中的标准 Alt 行为。

下面是一个简短的代码示例。请注意,没有任何类型的加速器/助记符冲突。

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.UIManager;
public class MnemonicTest extends JFrame {  
public MnemonicTest() {
    super("MnemonicTest");
    init();
}
public static void main(String[] args) throws Exception {
    MnemonicTest test = new MnemonicTest();
    test.setVisible(true);
}
private void init() {
    try {
         UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (Exception e1) {
        e1.printStackTrace();
    }
    setSize(new Dimension(500,400));
    JButton stopButton = new JButton("Stop");
    stopButton.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent e) {        
            System.exit(0);         
        }});
    this.getContentPane().setLayout(new BorderLayout());
    this.getContentPane().add(stopButton, BorderLayout.SOUTH);
    JMenuBar jMenuBar = new JMenuBar(); 
    JMenu menu = new JMenu("XXX");  
    JMenuItem a1 = new JMenuItem("a1", 'A');
    JMenuItem b1 = new JMenuItem("b1", 'B');
    JMenuItem c1 = new JMenuItem("c1", 'C');
    menu.add(a1);
    menu.add(b1);
    menu.add(c1);
    jMenuBar.add(menu);
    this.setJMenuBar(jMenuBar);
    JPanel p = new JPanel();
    ButtonGroup group = new ButtonGroup();
    p.add(new JTextField("XXXXXXXXXX"), BorderLayout.CENTER);
    JRadioButton but1 = new JRadioButton("test");
    but1.setMnemonic('s');
    JRadioButton but2 = new JRadioButton("2222");
    p.add(but1);
    p.add(but2);
    group.add(but1);
    group.add(but2);
    getContentPane().add(p, BorderLayout.CENTER);
}
}

我确实设法找到了一个有效的解决方案,即使它不完全是选美比赛的获胜者。如果您有更好的,请发布它!!

问题似乎是KeyEvent被发送到单选按钮,而不是窗格或文本字段。当系统看到 ALT 键已释放时,它会调用默认操作。

当然,当单选按钮具有焦点时,普通的原版 Alt 仍应执行其应执行的操作:激活菜单栏。

例如,如果您按下 Alt-S(我们的加速器),单选按钮将收到:keyPressed(Alt) -> keyPressed("S") -> keyRelease("S") -> keyRelease(Alt)。

因此,如果我们保存最后按下的键的值,我们将使用最后一个事件(keyRelease(Alt)),除非最后按下的键也是 Alt。

这是一种解决方法,

不是一个漂亮的解决方法,但它有效。代码如下(我在代码中保留了调试语句):

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.UIManager;
public class MnemonicTest extends JFrame {  
    int codeLast = 0;
    private final class RadioButtonKeyAdapter extends KeyAdapter {
        private static final int NO_CODE = 0;
        private int lastCode = 0;
        @Override
        public void keyPressed(KeyEvent e) {
            System.out.println("pressed source: " + e.getSource() + "n" + e.getKeyCode());
            this.setLastCode(e.getKeyCode());
        }
        @Override
        public void keyReleased(KeyEvent e) {
            System.out.println("released source: " + e.getSource() + "n" + e.getKeyCode());
            if (e.getKeyCode() == KeyEvent.VK_ALT && this.getLastCode() != e.getKeyCode()) {
                e.consume();
            }
            this.setLastCode(NO_CODE);
        }
        private int getLastCode() {
            return lastCode;
        }
        private void setLastCode(int lastCode) {
            this.lastCode = lastCode;
        }
    }
    private static final long serialVersionUID = 1L;
    public MnemonicTest() {
        super("MnemonicTest");
        init();
    }
    public static void main(String[] args) throws Exception {
        MnemonicTest test = new MnemonicTest();
        test.setVisible(true);
    }
    private void init() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        setSize(new Dimension(500,400));
        JButton stopButton = new JButton("Stop");
        stopButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {        
                System.exit(0);         
            }});
        this.getContentPane().setLayout(new BorderLayout());
        this.getContentPane().add(stopButton, BorderLayout.SOUTH);
        JMenuBar jMenuBar = new JMenuBar(); 
        JMenu menu = new JMenu("XXX");  
        JMenuItem a1 = new JMenuItem("a1", 'A');
        JMenuItem b1 = new JMenuItem("b1", 'B');
        JMenuItem c1 = new JMenuItem("c1", 'C');
        menu.add(a1);
        menu.add(b1);
        menu.add(c1);
        jMenuBar.add(menu);
        this.setJMenuBar(jMenuBar);
        JPanel p = new JPanel();
        ButtonGroup group = new ButtonGroup();
        JTextField textField = new JTextField("XXXXXXXXXX");
        p.add(textField, BorderLayout.CENTER);
        JRadioButton but1 = new JRadioButton("test");
        but1.setMnemonic('s');
        JRadioButton but2 = new JRadioButton("2222");
        p.add(but1);
        p.add(but2);
        group.add(but1);
        group.add(but2);
        getContentPane().add(p, BorderLayout.CENTER);
        but1.addKeyListener(new RadioButtonKeyAdapter());
        but2.addKeyListener(new RadioButtonKeyAdapter());
    }
}

相关内容

  • 没有找到相关文章

最新更新