简体   繁体   English

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

[英]Mnemonic behavior since Java 1.7 (still present in 1.8.0.121)

We have a legacy piece of software that runs on Java 1.6. 我们有一个在Java 1.6上运行的遗留软件。 When we finally got the green light to upgrade it to Java 1.8, the following problem manifested itself. 当我们最终获得绿灯将其升级到Java 1.8时,出现以下问题。

We have a set of radio buttons with accelerator keys. 我们有一组带加速键的单选按钮。 If a JTextComponent of any sort has the focus, and you hit one of the radio button accelerators (say, ALT-s), and you release the "s" before you release the ALT, the UIManager will activate the menu bar. 如果任何类型的JTextComponent具有焦点,并且您点击其中一个单选按钮加速器(例如,ALT-s),并且在释放ALT之前释放“s”,则UIManager将激活菜单栏。 (This only happens with the Windows look and feel) (这只发生在Windows的外观和感觉上)

Looks like a bug, and I've been thinking of writing a workaround by "consuming" the ALT release in those cases, but maybe someone has a better idea? 看起来像一个错误,我一直在考虑通过在这些情况下“消费”ALT版本来编写解决方法,但也许有人有更好的想法? Using a different look and feel is not an option, nor is switching off the standard Alt behavior in the UI Manager. 使用不同的外观不是一种选择,也不是在UI管理器中关闭标准的Alt行为。

Here's a short code sample. 这是一个简短的代码示例。 Note there are no accelerator/mnemonic conflicts of any sort. 请注意,没有任何类型的加速器/助记符冲突。

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);
}
}

I did manage to find a solution that works, even if it's not exactly a beauty contest winner. 我确实找到了一个有效的解决方案,即使它不是一个美容大赛冠军。 If you have a better one, please post it!! 如果你有一个更好的,请发布!!

The problem seems to be that the KeyEvent is sent to the radio button and not to the pane or the text field. 问题似乎是KeyEvent被发送到单选按钮而不是窗格或文本字段。 And when the system sees that the ALT key has been released, it invokes the default action. 当系统发现ALT键已被释放时,它会调用默认操作。

Of course, a plain vanilla Alt when a radio button has the focus should still do what it is supposed to do: activate the menu bar. 当然,当单选按钮具有焦点时,普通的香草色Alt应该仍然​​可以执行它应该执行的操作:激活菜单栏。

If you press, say, Alt-S (our accelerator), the radio button will receive: keyPressed(Alt) -> keyPressed("S") -> keyReleased("S") -> keyReleased(Alt). 如果你按下Alt-S(我们的加速器),单选按钮将收到:keyPressed(Alt) - > keyPressed(“S”) - > keyReleased(“S”) - > keyReleased(Alt)。

Thus, if we save the value of the last key pressed, we'll consume the last event (keyReleased(Alt)) unless the last key pressed was also an Alt. 因此,如果我们保存按下的最后一个键的值,我们将消耗最后一个事件(keyReleased(Alt)),除非按下的最后一个键也是Alt。

This is a workaround, and not a pretty one, but it works. 这是一种解决方法,而不是一个漂亮的解决方案,但它确实有效。 The code is as follows (I've left my debug statements in the code): 代码如下(我在代码中留下了我的调试语句):

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());
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM