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(); } } } } }