Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java KeyListener is Delayed Registration

As most people would by now realize with the key listener, is that in the keyPressed method, java registers one press of a key, then pauses for about half a second then records a steady stream of you pressing the key. My problem is that I would like java to immediately register a steady stream keyDown events as soon as a key is pressed down. I have read many articles on this problem and many of them speak of the swing timers, of which I am also in the dark about. To make the following piece of code nicer and easier to look at, I just trimmed out the fat and put the key listener into a separate class! Any input on this would be greatly appreciated!

public class Keyer implements KeyListener
{

    Keyer(){}
    @Override
    public void keyPressed(KeyEvent ovent)
    {
        int keyCode = ovent.getKeyCode();
        System.out.println("You pressed: "+keyCode);
    }
    @Override
    public void keyReleased(KeyEvent ovent)
    {

    }
    @Override
    public void keyTyped(KeyEvent ovent)
    {

    }
}
like image 785
Ben Hagel Avatar asked Dec 16 '22 20:12

Ben Hagel


2 Answers

If a key being pressed down is a trigger to update you GUI, that's when you need to incorperate SwingTimers. If it's not triggering an update to your GUI, you can just use normal threads.

What I assume the articles are referring to is

  1. Saving what keys are pressed using the KeyListener
  2. Using a Swing Timer check what keys are pressed at given intervals (say every 100ms)

Here's a sample of how you could incorporate that into your KeyListener:

    public class Keyer implements KeyListener{

        /** Stores currently pressed keys */
        HashSet<Integer> pressedKeys = new HashSet<Integer>();

        public Keyer(){

            //Check every 100ms if there's keys pressed
            //(This is the Swing Timer they talk about)
            new Timer(100, new ActionListener(){
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    String keysString = "";
                    if(!pressedKeys.isEmpty()){
                        Iterator<Integer> i = pressedKeys.iterator();
                        while(i.hasNext()){
                            keysString += i.next() + ",";
                        }
                    } 
                    System.out.println(keysString);
                }
            }).start();
        }

        @Override
        public void keyPressed(KeyEvent ovent){
            //Add key to hashSet when pressed
            int keyCode = ovent.getKeyCode();
            pressedKeys.add(keyCode);
        }
        @Override
        public void keyReleased(KeyEvent ovent){
            //Remove key from hashset when released
            int keyCode = ovent.getKeyCode();
            pressedKeys.remove(keyCode);
        }
        @Override
        public void keyTyped(KeyEvent ovent){}
    }

Here's a sample where everything is put together - a label is updated telling you exactly what keys are pressed (by keycode).

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

public class TempProject extends JPanel{
    /** Label to update with currently pressed keys */
    JLabel output = new JLabel();

    public TempProject(){
        super();
        setFocusable(true);
        add(output, BorderLayout.CENTER);
        requestFocus();
        addKeyListener(new Keyer());
    }

    public static void main(String args[])
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
                frame.setContentPane(new TempProject());    
                frame.pack();
                frame.setVisible(true);
                new TempProject();
            }
        });
    }

    public class Keyer implements KeyListener{

        /** Stores currently pressed keys */
        HashSet<Integer> pressedKeys = new HashSet<Integer>();

        public Keyer(){

            //Check every 100ms if there's keys pressed
            //(This is the Swing Timer they talk about)
            new Timer(100, new ActionListener(){
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    String keysString = "";
                    if(!pressedKeys.isEmpty()){
                        Iterator<Integer> i = pressedKeys.iterator();
                        while(i.hasNext()){
                            keysString += i.next() + ",";
                        }
                    } 
                    output.setText(keysString);
                }
            }).start();
        }

        @Override
        public void keyPressed(KeyEvent ovent){
            //Add key to hashSet when pressed
            int keyCode = ovent.getKeyCode();
            pressedKeys.add(keyCode);
        }
        @Override
        public void keyReleased(KeyEvent ovent){
            //Remove key from hashset when released
            int keyCode = ovent.getKeyCode();
            pressedKeys.remove(keyCode);
        }
        @Override
        public void keyTyped(KeyEvent ovent){}
    }



}

Edit

Also, please see warnings posted by @HovercraftFullOfEels about KeyListener. Depending on what you're trying to accomplish, you may want to look into using Key Bindings (same principle as this post applies). Here's a useful tutorial on Key Bindings if you'd like to take a look.

like image 75
Nick Rippe Avatar answered Jan 04 '23 17:01

Nick Rippe


I think that this is not a Java issue but rather an OS issue -- the OS delays in sending key strokes for a set period of time before sending a stream of key strokes. A solution is to start a Swing Timer on key press. I would also recommend that you use Key Bindings rather than a KeyListener.

Also you state that you are "in the dark" about Swing Timers, and if so, I urge you to do what the rest of us have done when in this situation: check the related tutorial. Google will help you find it quite quickly. Also a search of this site will help you find examples of using key bindings with a Swing Timer, for example, have a look here.

Edit
KeyListener is a low-level construct, and in general you should prefer to use higher-level constructs as they are safer and easier to use without causing side effects or having problems. For instance, it is easy to run into problems with focus issues if you use a KeyListener since it only works if the component listened to has the focus. This is easily circumvented if you used key bindings instead.

like image 32
Hovercraft Full Of Eels Avatar answered Jan 04 '23 17:01

Hovercraft Full Of Eels