argius note

プログラミング関連

JSpinnerがキーイベントを食い潰す

Java1.4だと困ってしまう問題。

private static void test(Window window) {
    Component[] components = {new JTextField(10),
                              new JSpinner(),
                              new JButton("SUBMIT")};
    JOptionPane pane = new JOptionPane(components,
                                       JOptionPane.PLAIN_MESSAGE,
                                       JOptionPane.OK_CANCEL_OPTION);
    pane.createDialog(window, "").setVisible(true);
}

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            JFrame frame = new JFrame();
            test(frame);
            frame.dispose();
        }
    });
}

このコードを実行すると、5つのコンポーネントが並んだダイアログが表示されます。それぞれコンポーネントにフォーカスを当てた状態でENTERキーかESCキーを押すと、ダイアログが閉じるはずです。但し、Java1.4で実行した場合、JSpinnerにフォーカスした状態で押しても反応しません。Java1.5以降では大丈夫です。


SUNのBugDatabaseでは、次のような報告があります。

この問題は1.4.1rcの時に報告され、修正されたのは5.0となっています。


回避策として、JSpinnerに直接KeyEvent処理を追加することが考えられますが、JSpinnerはKeyListenerを直接追加してもダメで、EditorのTextFieldに付けてあげる必要があるみたいです。

    JSpinner spinner = new JSpinner();
    Component[] components = {new JTextField(10),
                              spinner,
                              new JButton("SUBMIT")};
    JOptionPane pane = new JOptionPane(components,
                                       JOptionPane.PLAIN_MESSAGE,
                                       JOptionPane.OK_CANCEL_OPTION);
    final JDialog dialog = pane.createDialog(window, "");
    final DefaultEditor editor = (DefaultEditor)spinner.getEditor();
    editor.getTextField().addKeyListener(new KeyAdapter() {
        public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
                case KeyEvent.VK_ENTER:
                    try {
                        editor.commitEdit();
                    } catch (ParseException ex) {
                        ex.printStackTrace();
                    }
                    dialog.dispose();
                    break;
                case KeyEvent.VK_ESCAPE:
                    dialog.dispose();
                    break;
                default:
            }
        }
    });
    dialog.setVisible(true);