Swing, слушаем нажатия клавиш
Для прослушивания нажатий клавиш, существует специальный интерфейс KeyListener:
interface KeyListener {
//клавиша нажата, но не отпущена
public void keyPressed(KeyEvent event);
//клавиша отпущена
public void keyReleased(KeyEvent event);
//клавиша нажата и отпущена
public void keyTyped(KeyEvent event);
}
Каждый метод, реализованный интерфейсом KeyListener, вызывается определенным событием, вместе с которым передается экземпляр KeyEvent. KeyEvent содержит в себе всю информацию о нажатой клавише и о модификаторах, таких как Alt, Ctrl, Shift:
int keyCode = event.getKeyCode(); //цифровой код нажатой клавиши boolean isAltDown = event.isAltDown(); boolean isControlDown = event.isControlDown(); boolean isShiftDown = event.isShiftDown();
Дополнительно про KeyEvent можно почитать тут.
У каждой клавиши есть свой цифровой код, например, код пробела 32, клавиша вправо имеет код 39. Всегда можно посмотреть эти коды, выполнив команду:
System.out.println(event.getKeyCode());
Кроме того, класс KeyEvent содержит коды всех клавиш в статических переменных, все они начинаются на VK_
KeyEvent.VK_SPACE; //32 KeyEvent.VK_RIGHT; //39
Можно и нужно использовать эти переменные для сравнения:
If (event.getKeyCode()==KeyEvent.VK_SPACE) {
System.out.println(Нажат пробел);
}
Короткие нажатия
Когда дело касается только обработки нажатой клавиши, достаточно поместить необходимый код в метод keyTyped() интерфейса KeyListener.
@Override
public void keyTyped(KeyEvent event) {
//клавиша нажата и отпущена
}
Длинные одновременные нажатия
Другое дело, когда нам необходимо обрабатывать не только нажатие, но еще и его длительность и скорее всего сразу у нескольких клавиш одновременно. В этом случае приходится вводить дополнительные переменные, на каждую отслеживаемую клавишу:
private boolean isLeft = false; private boolean isRight = false; private boolean isUp = false; private boolean isDown = false;
Такой подход позволяет реализовывать составные действия, например, длительное движение вправо-вверх одновременно. Необходимо правильно отлавливать события с клавиатуры. Когда зажата клавиша, мы получаем событие в метод keyPressed и записываем эту информацию в переменную. С этого момента мы считаем, что клавиша непрерывно нажата. Если клавиша будет отпущена, мы получим событие в метод keyReleased и обновим об этом информацию в переменной.
@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:
frame.addKeyListener(keyListener);
Живой пример
Перед вами код, реализующий отрисовку змейки. Голова управляется "стрелками".
Листинг RunKeybord.java:
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();
}
}
}
}
}
