2

I'm trying to implement a KeyListener for a JFrame form in java. So far, I used the code suggested here

and got a fairly good result, but when the event fires (when any of the desired keys is pressed), it seems that not only do I get the last key pressed, but as the occurrences continue, I get 2, 3, etc, values of keys (which can be seen by the ammount of JOptionPane.showMessageDialog() displayed).

How can I limit the evaluation of the event to JUST the last key pressed? or, how can I "clear" the key pressed array each time the event fires?

Here is the full code for my form:

import java.awt.event.*;
import javax.swing.KeyStroke;
import javax.swing.Action;
import javax.swing.JOptionPane;
import java.awt.KeyEventDispatcher;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;


public class frmMain extends javax.swing.JFrame {
    char optionselected;
    String other;
    private class MyDispatcher implements KeyEventDispatcher {
        @Override
        public boolean dispatchKeyEvent(KeyEvent e) {
            if (e.getID() == KeyEvent.KEY_RELEASED) { optionselected = e.getKeyChar(); }
            other = String.valueOf(optionselected).toUpperCase();
            switch (other) {
                case "C": JOptionPane.showMessageDialog(null, "Option 1"); break;
                case "T": JOptionPane.showMessageDialog(null, "Option 2"); break;
                case "D": JOptionPane.showMessageDialog(null, "Option 3"); break;
                case "N": JOptionPane.showMessageDialog(null, "Option 4"); break;
                case "O": JOptionPane.showMessageDialog(null, "Option 5"); break;
                case "S": JOptionPane.showMessageDialog(null, "Option 6"); break;
                default: break;
            }
            return false;
        }
    }    
    /**
     * Creates new form frmMain
     */
    public frmMain() {
        initComponents();
        KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        manager.addKeyEventDispatcher(new MyDispatcher());     
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jInternalFrame1 = new javax.swing.JInternalFrame();
        jDesktopPane1 = new javax.swing.JDesktopPane();
        mbarMenu = new javax.swing.JMenuBar();
        mnuConta = new javax.swing.JMenu();
        mnuTeso = new javax.swing.JMenu();
        mnuDiezmo = new javax.swing.JMenu();
        mnuNomi = new javax.swing.JMenu();
        mnuCole = new javax.swing.JMenu();
        mnuExit = new javax.swing.JMenu();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setMaximumSize(new java.awt.Dimension(800, 600));
        setMinimumSize(new java.awt.Dimension(800, 600));
        setPreferredSize(new java.awt.Dimension(800, 600));
        setResizable(false);

        jInternalFrame1.setMaximumSize(new java.awt.Dimension(1000, 607));
        jInternalFrame1.setMinimumSize(new java.awt.Dimension(1000, 607));
        jInternalFrame1.setPreferredSize(new java.awt.Dimension(1000, 607));
        jInternalFrame1.setVisible(true);

        mnuConta.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/books_noun_9566_cc.png"))); // NOI18N
        mnuConta.setText("[C] = Option 1");
        mnuConta.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        mbarMenu.add(mnuConta);

        mnuTeso.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/money_noun_197480_cc.png"))); // NOI18N
        mnuTeso.setText("[T] = Option 2");
        mnuTeso.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        mbarMenu.add(mnuTeso);

        mnuDiezmo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/diezmador_noun_247665_cc.png"))); // NOI18N
        mnuDiezmo.setText("[D] = Option 3");
        mnuDiezmo.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        mbarMenu.add(mnuDiezmo);

        mnuNomi.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/payroll_noun_233106_cc.png"))); // NOI18N
        mnuNomi.setText("[N] = Option 4");
        mnuNomi.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        mbarMenu.add(mnuNomi);

        mnuCole.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/school_noun_147545_cc.png"))); // NOI18N
        mnuCole.setText("[O] = Option 5");
        mnuCole.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        mbarMenu.add(mnuCole);

        mnuExit.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/logout_noun_14841_cc.png"))); // NOI18N
        mnuExit.setText("[S] = Option 6");
        mnuExit.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        mnuExit.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                mnuExitMouseClicked(evt);
            }
        });
        mbarMenu.add(mnuExit);

        jInternalFrame1.setJMenuBar(mbarMenu);

        javax.swing.GroupLayout jInternalFrame1Layout = new javax.swing.GroupLayout(jInternalFrame1.getContentPane());
        jInternalFrame1.getContentPane().setLayout(jInternalFrame1Layout);
        jInternalFrame1Layout.setHorizontalGroup(
            jInternalFrame1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jDesktopPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 989, Short.MAX_VALUE)
        );
        jInternalFrame1Layout.setVerticalGroup(
            jInternalFrame1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jDesktopPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 539, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jInternalFrame1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jInternalFrame1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>                        

    private void mnuExitMouseClicked(java.awt.event.MouseEvent evt) {                                     
        this.dispose();
        System.exit(0);
    }                                    

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(frmMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(frmMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(frmMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(frmMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new frmMain().setVisible(true);
            }
        });
    }

    public static void salida(){
        System.exit(0);
    }

    // Variables declaration - do not modify                     
    private javax.swing.JDesktopPane jDesktopPane1;
    private javax.swing.JInternalFrame jInternalFrame1;
    private javax.swing.JMenuBar mbarMenu;
    private javax.swing.JMenu mnuCole;
    private javax.swing.JMenu mnuConta;
    private javax.swing.JMenu mnuDiezmo;
    private javax.swing.JMenu mnuExit;
    private javax.swing.JMenu mnuNomi;
    private javax.swing.JMenu mnuTeso;
    // End of variables declaration                   
}

The MyDispatcher class implements the KeyListener, and is supposed to raise a JOptionPane.showMessageDialog() only once for each time a desired key is pressed.

Thank you in advance for any guidance on the subject.

Community
  • 1
  • 1
  • 3
    Short answer is, use the Key Bindings API, see [How to Use Key Bindings](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html) for more details – MadProgrammer Dec 21 '15 at 07:32
  • Possible duplicate of [How to use Key Bindings instead of Key Listeners](http://stackoverflow.com/questions/22741215/how-to-use-key-bindings-instead-of-key-listeners) – user1803551 Dec 21 '15 at 11:20

2 Answers2

3

From the documentation for dispatchKeyEvent:

Return true if the KeyboardFocusManager should take no further action with regard to the KeyEvent; false otherwise

Also you can directly consume the event in your dispatchKeyEvent implementation when no further processing of the event is required.

e.consume();

Suggestion of @MadProgrammer is also important. Using of global event processing possibilities is the last choice.

hichris123
  • 10,145
  • 15
  • 56
  • 70
Sergiy Medvynskyy
  • 11,160
  • 1
  • 32
  • 48
  • Given the fact that the OP is changing the state of the `optionselected` ONLY when on `KEY_RELESED`, consuming the event or even changing the return value will have little effect, as the dialog is been shown based on the `optionselected` state – MadProgrammer Dec 21 '15 at 07:42
2

The basic problem is, you are getting both a key pressed and key released event, but you are only changing optionselected on key release.

So:

  • Pressing C, on keyPressed, optionSelected is null, other will be null, on keyReleased, optionSelected becomes C, show dialog
  • Pressing T, on keyPressed optionselected is (still) C, show dialog, on keyReleased optionselected becomes T, show dialog

Instead, you should be processing only the keyReleased event

private class MyDispatcher implements KeyEventDispatcher {

    @Override
    public boolean dispatchKeyEvent(KeyEvent e) {
        if (e.getID() == KeyEvent.KEY_RELEASED) {
            optionselected = e.getKeyChar();
            other = String.valueOf(optionselected).toUpperCase();
            switch (other) {
                case "C":
                    JOptionPane.showMessageDialog(null, "Option 1");
                    break;
                case "T":
                    JOptionPane.showMessageDialog(null, "Option 2");
                    break;
                case "D":
                    JOptionPane.showMessageDialog(null, "Option 3");
                    break;
                case "N":
                    JOptionPane.showMessageDialog(null, "Option 4");
                    break;
                case "O":
                    JOptionPane.showMessageDialog(null, "Option 5");
                    break;
                case "S":
                    JOptionPane.showMessageDialog(null, "Option 6");
                    break;
                default:
                    break;
            }
        }
        return false;
    }
}

Having said all that, KeyEventDispatcher is very, very low level, a better solution might be to use the key bindings API, see How to Use Key Bindings for more details

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366