我们有一个在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());
}
}