Я разрабатываю сложное музыкальное приложение в java (8) на основе Netbeans RCP 8.2, и у меня возникает странная проблема, которая возникает случайно.
У меня есть JFrame с панелью, которая содержит много JComponents. Я использую InputMap / ActionMap панели для захвата нажатий клавиш «a», «b», ..., «g» и вызова действия.
Действие извлекает ключевой символ, затем показывает JDialog, который содержит JTextField, используемый для редактирования некоторых текстовых данных.
Перед отображением диалога с dialog.setVisible (true), действие вызывает dialog.prepare (ключ char), чтобы JDialog мог инициализировать себя перед отображением. На самом деле dialog.prepare (ключ char) добавляет только переданный символ (преобразованный в верхний регистр) в JTextField.
Это работает большую часть времени: я нажимаю, например, "c" в JFrame, затем JDialog появляется с "C" в конце JTextField.
Но иногда, может быть 1/20 раз, я получаю "Cc" в конце поля JTextfield!
Это похоже на то, что исходное событие нажатия клавиши (которое приходит из JComponent на панели JFrame и было обработано с использованием InputMap / ActionMap) также было избыточно обработано JDialog.
Я убедился, что это не аппаратная проблема клавиатуры. Я воспроизвел проблему на втором компьютере с Win8 (у меня Win10).
Я безуспешно пытался 1 / использовать KeyListener вместо InputMap / ActionMap
и 2 / используйте java.awt.EventQueue.invokeLater () для добавления ключевого символа в JTextField.
Я создал небольшое независимое приложение (см. Ниже), чтобы воспроизвести проблему и облегчить отладку ... но это небольшое приложение работает нормально, я не мог воспроизвести проблему :-( Затем я сравнил снова с моим реальным кодом приложения, и это действительно тот же код, за исключением того, что настоящее приложение представляет собой законченное приложение RCP Netbeans.
Так может ли быть, что Netbeans RCP влияет на то, как Swing обрабатывает ключевые события? Это выглядит странно для меня ...
Я потерян, любая подсказка / предлагаемый тест будет принята с благодарностью!
/**
* Try to reproduce double key problem... Failed because this works OK !! :-(
*/
public class PbKeyDouble extends JFrame {
MyDialog dialog;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
PbKeyDouble o = new PbKeyDouble();
o.setVisible(true);
}
});
}
public PbKeyDouble() {
// GUI INITIALIZATION
// Add a basic panel
JPanel panel = new JPanel();
getContentPane().add(panel);
panel.setPreferredSize(new Dimension(300, 200));
JButton button = new JButton("BUTTON");
panel.add(button);
// Button not used, it's only to simulate the real app where a component in the panel has the focus
button.requestFocusInWindow();
// If "A" or "B" key is pressed anywhere, MyAction.actionPerformed() will be called
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("A"), "MyAction");
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("B"), "MyAction");
panel.getActionMap().put("MyAction", new MyAction());
// Prepare JFrame
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
}
private class MyAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("EDT? " + SwingUtilities.isEventDispatchThread()); // Always prints TRUE
if (dialog == null) {
dialog = new MyDialog();
}
// Retrieve the key used to trigger the action
char c = e.getActionCommand().charAt(0);
// Prepare the dialog (insert the char)
dialog.prepare(c);
// Show dialog
dialog.setVisible(true);
}
}
private class MyDialog extends JDialog {
JTextField textfield;
/**
* A simple dialog with just a textfield.
*/
public MyDialog() {
textfield = new JTextField("Hello");
textfield.setColumns(100);
getContentPane().add(textfield);
pack();
setLocationRelativeTo(null);
}
/**
* Append the key (uppercased) at the end of the textfield
*/
public void prepare(char c) {
String text = textfield.getText();
textfield.setText(text + " " + Character.toUpperCase(c));
}
/**
* Overridden to add a global key binding on ESC key to exit the dialog.
* <p>
* This is only to facilitate the test where I need to try many times the process pressing "a" ESC "a" ESC etc.
*
* @return
*/
@Override
protected JRootPane createRootPane() {
JRootPane contentPane = new JRootPane();
contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("ESCAPE"), "actionCancel");
contentPane.getActionMap().put("actionCancel", new AbstractAction("Cancel") {
@Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
return contentPane;
}
}
}