Для прослушивания нажатий клавиш, существует специальный интерфейс KeyListener:

1
2
3
4
5
6
7
8
9
10
interface KeyListener {
    //клавиша нажата, но не отпущена
    public void keyPressed(KeyEvent event);
 
    //клавиша отпущена
    public void keyReleased(KeyEvent event);
 
    //клавиша нажата и отпущена
    public void keyTyped(KeyEvent event);
}

Каждый метод, реализованный интерфейсом KeyListener, вызывается определенным событием, вместе с которым передается экземпляр KeyEvent. KeyEvent содержит в себе всю информацию о нажатой клавише и о модификаторах, таких как Alt, Ctrl, Shift:

1
2
3
4
int keyCode = event.getKeyCode(); //цифровой код нажатой клавиши
boolean isAltDown = event.isAltDown();
boolean isControlDown = event.isControlDown();
boolean isShiftDown = event.isShiftDown();

Дополнительно про KeyEvent можно почитать тут.

У каждой клавиши есть свой цифровой код, например, код пробела 32, клавиша вправо имеет код 39. Всегда можно посмотреть эти коды, выполнив команду:

1
System.out.println(event.getKeyCode());

Кроме того, класс KeyEvent содержит коды всех клавиш в статических переменных, все они начинаются на VK_

1
2
KeyEvent.VK_SPACE; //32
KeyEvent.VK_RIGHT; //39

Можно и нужно использовать эти переменные для сравнения:

1
2
3
If (event.getKeyCode()==KeyEvent.VK_SPACE) {
    System.out.println(Нажат пробел);
}

Короткие нажатия

Когда дело касается только обработки нажатой клавиши, достаточно поместить необходимый код в метод keyTyped() интерфейса KeyListener.

1
2
3
4
@Override
public void keyTyped(KeyEvent event) {
    //клавиша нажата и отпущена
}

Длинные одновременные нажатия

Другое дело, когда нам необходимо обрабатывать не только нажатие, но еще и его длительность и скорее всего сразу у нескольких клавиш одновременно. В этом случае приходится вводить дополнительные переменные, на каждую отслеживаемую клавишу:

1
2
3
4
private boolean isLeft = false;
private boolean isRight = false;
private boolean isUp = false;
private boolean isDown = false;

Такой подход позволяет реализовывать составные действия, например, длительное движение вправо-вверх одновременно. Необходимо правильно отлавливать события с клавиатуры. Когда зажата клавиша, мы получаем событие в метод keyPressed и записываем эту информацию в переменную. С этого момента мы считаем, что клавиша непрерывно нажата. Если клавиша будет отпущена, мы получим событие в метод keyReleased и обновим об этом информацию в переменной.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public void keyPressed(KeyEvent event) {
    if (e.getKeyCode()==KeyEvent.VK_LEFT) isLeft = true;
    if (e.getKeyCode()==KeyEvent.VK_RIGHT) isRight = true;
    if (e.getKeyCode()==KeyEvent.VK_UP) isUp = true;
    if (e.getKeyCode()==KeyEvent.VK_DOWN) isDown = true;
}
 
@Override
public void keyReleased(KeyEvent event) {
    if (e.getKeyCode()==KeyEvent.VK_LEFT) isLeft = false;
    if (e.getKeyCode()==KeyEvent.VK_RIGHT) isRight = false;
    if (e.getKeyCode()==KeyEvent.VK_UP) isUp = false;
    if (e.getKeyCode()==KeyEvent.VK_DOWN) isDown = false;
}

Некий движок, управляющий нашей программой и живущий в отдельном потоке не слушает нажатия клавиш напрямую. Вместо этого, он работает с переменными, которые мы любезно для него подготовили.

Подключаем слушатель

Остается только повесить наш класс слушателя нажатий на какой-нибудь компонент Swing, например на JFrame:

1
frame.addKeyListener(keyListener);

Живой пример

Перед вами код, реализующий отрисовку змейки. Голова управляется "стрелками".

Змейка на Java Swing в 100 строк кода Змейка на Java Swing в 100 строк кода

Листинг RunKeybord.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package ru.jcup.education.graphics.swing;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.JFrame;
 
public class RunKeyboard extends JFrame implements KeyListener{
 
    private Thread thread;
    private static Random random = new Random();
    private static final int DIR_STEP = 2;
    private boolean isLeft = false;
    private boolean isRight = false;
    private boolean isUp = false;
    private boolean isDown = false;
    private int x, y;
     
    public RunKeyboard(int width, int height) {
        this.setSize(width, height);
        x = width/2;
        y = height/2;
        this.addKeyListener(this);
        thread = new MoveThread(this);
        thread.start();
    }
     
    //Start point
     
    public static void main(String... string) {
        JFrame frame = new RunKeyboard(500,500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
     
    //Listener
     
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode()==KeyEvent.VK_LEFT) isLeft = true;
        if (e.getKeyCode()==KeyEvent.VK_RIGHT) isRight = true;
        if (e.getKeyCode()==KeyEvent.VK_UP) isUp = true;
        if (e.getKeyCode()==KeyEvent.VK_DOWN) isDown = true;
    }
 
    @Override
    public void keyReleased(KeyEvent e) {
        if (e.getKeyCode()==KeyEvent.VK_LEFT) isLeft = false;
        if (e.getKeyCode()==KeyEvent.VK_RIGHT) isRight = false;
        if (e.getKeyCode()==KeyEvent.VK_UP) isUp = false;
        if (e.getKeyCode()==KeyEvent.VK_DOWN) isDown = false;
    }
 
    @Override
    public void keyTyped(KeyEvent arg0) {}
     
    //Graphics
     
    @Override
    public void paint(Graphics gr) {
        Graphics2D g2d = (Graphics2D)gr;
        int r = random.nextInt(256);
        int g = random.nextInt(256);
        int b = random.nextInt(256);
        g2d.setColor(new Color(r,g,b));
        g2d.setStroke(new BasicStroke(4f));
        g2d.drawOval(x-25, y-25, 50, 50);
    }
     
    public void animate() {
        if (isLeft) x-=DIR_STEP;
        if (isRight) x+=DIR_STEP;
        if (isUp) y-=DIR_STEP;
        if (isDown) y+=DIR_STEP;
        this.repaint();
    }
     
    //Engine thread
     
    private class MoveThread extends Thread{
        RunKeyboard runKeyboard;
         
        public MoveThread(RunKeyboard runKeyboard) {
            super("MoveThread");
            this.runKeyboard = runKeyboard;
        }
         
        public void run(){
            while(true) {
                runKeyboard.animate();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 
}