Главная » Java, Web » События клавиатуры

0

Все события Java связаны с компонентами графического интерфейса пользователя. При нажатии кнопки мыши, например, возникающие события связаны с тем компонентом, над которым располагается курсор мыши. А что происходит с событиями клавиатуры? С каким компонентом будет связано событие клавиатуры, например, событие нажатия клавиши?

В программировании графического интерфейса пользователя есть понятие фокус. В каждый момент времени только один элемент имеет на себе фокус. Фокус внимания может быть сконцентрирован только на одном элементе, и если этот элемент является компонентом Java, то все события клавиатуры будут представлены в виде объектов Java с типом KeyEvent и переданы про- слушивателю событий этого типа, связанных с тем компонентом, на котором установлен фокус. Если тот или иной компонент требует получения фокуса, то для этого используется метод requestFocus (), этот метод определен в классе component. Вызов этого метода, однако, не дает гарантии того, что фокус будет помещен на компоненте, вызывающем фокус. В обычной ситуации фокус может быть передан компоненту путем щелчка мыши на этом компоненте или с помощью последовательного перемещения фокуса с одного элемента на другой при нажатии клавиши табуляции.

Некоторые компоненты все же не получают фокус даже в том случае, когда на них щелкают мышью. Чтобы решить эту проблему, можно вручную регистрировать события мыши. В ответ на щелчок мыши в методе mousePressed () следует вызвать requestFocus (). Такой метод работает, в частности, с компонентами, которые используются в качестве "холстов" для рисования (мы встречались с ними ранее). Например, создается "холст" — объект класса JPanel с именем hoist. Чтобы прослушивать события мыши, "холст" должен зарегистрировать прослушиватель событий и в методе mousePressed о объекта прослушивателя вызвать drawingSurface.requestFocus() .

В качестве примера можно привести апплет, который прослушивает события мыши и переводит фокус на компонент, на котором щелкнула мышь. Затем компонент запрашивает фокус. Если фокус установлен на компоненте, то апплет позволяет перетаскивать цветной квадрат. Апплет реагирует на события клавиатуры, изменяя цвет этого квадрата на красный, зеленый, синий или черный в соответствии с тем, какая из четырех клавиш <R>, <G>, <В> или <К> была нажата на клавиатуре (листинг 1.21).

Листинг 1.21. Файл Focus.java

Апплет для демонстрации событий клавиатуры и установки фокуса . Нажимая клавиши клавиатуры со стрелками, можно перемещать квадрат в соответствующие стороны.

Нажатие клавиш <R>, <G>, <В>, <К> приводит к изменению цвета квадрата (красный, зеленый, синий, черный соответственно).

impo гt j ava.awt.*;

impo rt j ava.awt.event.*;

import javax.swing. *;

public class Focus extends JApplet

implements KeyListener, FocusListener, MouseListener { // прослушиватель MouseListener используется только для того, // чтобы апплет мог по щелчку мыши установить на себе фокус static final int SQUARE_SIZE = 40; // сторона квадрата Color squareColor;       // цвет квадрата

int squareTop, squareLeft;          // координаты верхнего левого угла

// квадрата

boolean focussed = false;          // true, если фокус на апплете

DisplayPanel canvas;               // холст для рисования в апплете

public void init() {

// инициализация апплета

squareTop = getSize().height / 2 – SQUARE_SIZE / 2; squareLeft = getSize().width / 2 – SQUARE_SIZE / 2; squareColor = Color.red;

canvas = new DisplayPanel();             // создание холста

setContentPane(canvas);

canvas.setBackground(Color.white); // цвет фона холста canvas.addFocusListener(this); // регистрация прослушивателей

canvas.addKeyListener(this); canvas.addMouseListener(this); class DisplayPanel extends JPanel { // Описание холста public void paintComponent(Graphics g) {

super.paintComponent(g); // заполнение холста цветом фона(белый) /*

Рисование границы, толщиной в три пиксела. Цвет зависит от того, есть ли на апплете фокус. */

if (focussed) g.setColor(Color.cyan); else

g.setColor(Color.lightGray); int width = getSize().width; // ширина апплета int height = getSize().height; // высота апплета g.drawRect(0,0,width-1,height-1); g.drawRect(1,1,width-3,height-3) ; g.drawRect(2,2,width-5,height-5) ;

// рисовать квадрат g.setColor(squareColor);

g.fillRect(squareLeft, squareTop, SQUARE_SIZE, SQUARE_SIZE) , // вывод сообщения о том, что фокуса нет if (!focussed) { g.setColor(Color.magenta);

g.drawString("Navodim FOCUS", 7, 20) ; }

} }

// методы обработки событий

public void focusGained(FocusEvent evt) {

focussed = true;

canvas.repaint(); }

public void focusLost(FocusEvent evt) { // фокус потерян focussed = false;

canvas.repaint(); // перерисовка границ апплета }

public void keyTyped(KeyEvent evt) { // Напечатан символ во время удержания фокуса. // Если символ соответствует символу задания цвета, // то апплет меняет цвет квадрата. char ch = evt.getKeyChar(); // символ if (ch == ‘В’ || ch == ‘b’) { squareColor = Color.blue; canvas.repaint();

}

else if (ch == ‘G’ || ch == ‘g’) { squareColor = Color.green; canvas.repaint();

}

else if (ch == ‘R’ || ch == ‘г’) { squareColor = Color.red; canvas.repaint();

}

else if (ch == ‘К’ || ch == ‘k’) { squareColor = Color.black;

canvas.repaint() ;

}

}

public void keyPressed(KeyEvent evt) { // передвижение квадрата при нажатии // клавиш стрелок

int key = evt.getKeyCode(); // код нажатой клавиши if (key == KeyEvent.VK_LEFT) { squareLeft -= 8; if (squareLeft < 3) squareLeft = 3; canvas.repaint();

}

else if (key == KeyEvent.VK_RIGHT) { squareLeft += 8;

if (squareLeft > getSize().width – 3 – SQUARE_SIZE) squareLeft = getSize().width – 3 – SQUARE_SIZE; canvas.repaint();

}

else if (key == KeyEvent.VKJJP) { squareTop -= 8; if (squareTop < 3) squareTop = 3; canvas.repaint();

}

else if (key == KeyEvent.VK_DOWN) { squareTop += 8;

if (squareTop > getSize().height – 3 – SQUARE_SIZE) squareTop = getSize().height – 3 – SQUARE_SIZE; canvas.repaint();

}

}

public void keyReleased(KeyEvent evt) { }

public void mousePressed(MouseEvent evt) { // при щелчке на апплете требуется установить // фокус на компоненте холста

canvas.requestFocus(); }

public void mouseEntered(MouseEvent evt) { } public void mouseExited(MouseEvent evt) { } public void mouseReleased(MouseEvent evt) { } public void mouseClicked(MouseEvent evt) { }

}

Если апплет не активирован, он обведен серой рамкой и на нем написана фраза "Navodim FOCUS". Далее мы разберем некоторые детали, с которыми мы имеем дело в этом апплете.

В языке Java события клавиатуры принадлежат классу KeyEvent. Чтобы объект был прослушивателем событий клавиатуры, он должен имплементиро- вать интерфейс KeyListener. Затем объект прослушивателя должен быть вставлен в компонент, с которым связаны события клавиатуры, с помощью метода addKeyListener (): component.addKeyListener(listener);

В интерфейсе KeyListener определены следующие методы:

public void keyPressed(KeyEvent evt); public void keyReleased(KeyEvent evt); public void keyTyped(KeyEvent evt);

Следует иметь в виду, что существует разница между событием нажатия клавиши и событием печати символа. На клавиатуре компьютера размещено большое количество клавиш. Это клавиши букв, клавиши цифр, клавиши модификаторов (например, <Shift>+<Ctrl>), клавиши стрелок, функциональные клавиши и пр. Во многих случаях нажатие клавиши не связано с печатью символов. С другой стороны, печать символа иногда требует нажатия нескольких клавиш. Например, чтобы напечатать символ r, необходимо нажать клавишу <Shift>, и до того, как эта клавиша будет отпущена, нажать клавишу <R>. Java располагает тремя типами событий клавиатуры, которые соответствуют нажатию клавиши, отпусканию клавиши и печати символа. Нажатию клавиши соответствует метод keyPressed, при отпускании клавиши вызывается метод keyReleased, а при печати символа вызывается метод keyTyped. Иногда одно действие приводит к появлению нескольких событий, например, событие печати символа r также генерирует два события keyPressed, два события keyReleased И ОДНО событие keyTyped.

Удобнее всего мысленно разделить эти события на два независимых потока: первый поток — события keyPressed и keyReleased, второй поток — события печати символов keyTyped. В некоторых приложениях нужно будет работать только с событиями первого потока, в других приложениях более полезной окажется работа с событиями печати символов. Понятно, что из событий первого потока можно извлечь ту информацию, которая содержится в событиях второго потока, однако это не так уж просто. Иногда полезными оказываются события keyPressed. Нажатая клавиша может удерживаться долгое время. При этом, однако, появляется небольшая проблема, состоящая в том, что при удержании клавиши в нажатом состоянии генерируется серия событий keypressed, кроме того, может быть сгенерирована и серия событий keyTyped. Часто это оказывается не слишком важным. Следует, однако, помнить, что не каждое событие keypressed приводит к появлению события keyReleased (клавиша отпущена).

Каждая клавиша на клавиатуре имеет связанное с ней числовое значение, соответсвующее коду символа. При вызове методов keypressed или keyReleased передаваемый им аргумент типа события содержит соответствующее нажатой или отпущенной клавише числовое значение. Этот код может быть получен при помощи вызова функции evt.getKeyCode (). В Java вместо таблицы с ASCII-кодами используются именные константы, которые ставятся в соответствие каждой клавише на клавиатуре. Константы эти определены в классе KeyEvent. Так, например, для клавиши <Shift> эта константа будет KeyEvent.VK_SHiFT. Если необходимо произвести проверку того, была ли нажата клавиша <Shift>, то следует задать условие:

if (evt.getKeyCode() == KeyEvent.VK_SHIFT)

Рис. 1.21. Простая игра в виде апплета

Часто при работе с событиями KeyTyped необходимо узнать, какая именно клавиша была нажата. Это делается при помощи функции evt.getKeyChar о. В приведенном ранее примере обработка событий клавиатуры производится с использованием метода keypressed, а изменение цветов квадрата посредством keyTyped.

Важным при работе с графическим интерфейсом пользователя является понятие состояния. Существует даже понятие "машина состояний". В нашем примере также использовались состояния. Например, записывалось состояние апплета, сохраняя при этом информацию о том, имеет ли апплет установленный на нем фокус, а также другие параметры, а именно переменные focussed, squareLeft, squareTop. Эти переменные состояния были использованы в методе paintComponent () при определении того, как будет выглядеть апплет.

Приведем еще один пример апплета (листинги 1.22 и 1.23), где состояние играет еще большую роль. Этот апплет предлагает аркадную игру (рис. 1.21).

Листинг 1.22. Файл Framework.java

impo гt j ava.awt.*; impo rt j ava.awt.event.*; import javax.swing. *;

public class Framework extends JApplet

implements ActionListener, KeyListener, FocusListener, MouseListener {

protected void dolnitialization(int width, int height) { }

protected void drawFrame(Graphics g, int width, int height) { g.setColor(Color.lightGray); g.fillRect(0,0,width,height); g.setColor(Color.black);

g.drawstring("Elapsed Time: " + (getElapsedTime()/1000),10,20);

g.drawstring("Frame Number: " + (getFrameNumber()),10,35); }

public void keyTyped(KeyEvent evt) { }

public void keypressed(KeyEvent evt) { }

public void keyReleased(KeyEvent evt) { }

public int getFrameNumber() { return frameNumber;

}

public void setFrameNumber(int frameNumber) { if (frameNumber < 0)

this.frameNumber = 0; else

this. frameNumber = frameNumber; }

public long getElapsedTime () {

return elapsedTime; }

public void setFrameCount(int max) { if (max <= 0)

this.frameCount = -1; else

frameCount = max; }

public void setMillisecondsPerFrame(int time) { millisecondsPerFrame = time; if (timer != null)

timer.setDelay(millisecondsPerFrame); }

public void setFocusBorderColor(Color c) {

focusBorderColor = c; }

private int frameNumber = 0; private int frameCount = -1; private int millisecondsPerFrame = 40; private long startTime; private long oldElapsedTime; private long elapsedTime; private Timer timer; private JPanel frame; private boolean focussed = falser- Color focusBorderColor = Color.cyan; public Framework() { frame = new JPanel() {

public void paintComponent(Graphics g) { int width = getSize().width; int height = getSize().height;

drawFrame(g,width,height); if (focussed)

g.setColor(focusBorderColor); else

g.setColor(Framework.this.getBackground()); g.drawRect(0,0,width-1,height-1) ; g.drawRect(1,1,width-3,height-3) ;

g.drawRect(2,2,width-5,height-5); if (!focussed) { g.setColor(Framework.this.getForeground()); g.drawString("Click to activate",10,height-12);

}

}

};

setContentPane(frame); setBackground(Color.gray) ; setForeground(Color.red);

frame.setFont(new Font("SanSerif",Font.BOLD,14)); frame.addFocusListener(this); frame.addKeyListener(this);

addMouseListener(this) ; }

public void init() {

dolnitialization(getSize().width, getSize().height); }

public void actionPerformed(ActionEvent evt) { frameNumber++;

if (frameCount > 0 && frameNumber >= frameCount) frameNumber = 0;

elapsedTime = oldElapsedTime + (System.currentTimeMillis() — startTime);

frame.repaint(); }

private void startAnimation() { if (focussed) { if (timer == null) { timer = new Timer(millisecondsPerFrame, this); timer.start();

}

else

timer.restart() ;

startTime = System.currentTimeMillis();

}

}

private void stopAnimation() { if (focussed) {

oldElapsedTime += (System.currentTimeMillis() — startTime); elapsedTime = oldElapsedTime; frame.repaint(); timer.stop();

}

}

public void start() {

startAnimation(); }

public void stop() {

stopAnimation(); }

public void focusGained(FocusEvent evt) { focussed = true;

startAnimation(); }

public void focusLost(FocusEvent evt) { stopAnimation();

focussed = false; }

public void mousePressed(MouseEvent evt) {

frame.requestFocus(); }

public void mouseEntered(MouseEvent evt) { } public void mouseExited(MouseEvent evt) { } public void mouseReleased(MouseEvent evt) { } public void mouseClicked(MouseEvent evt) { }

Листинг 1.23. Файл Game.java

impo rt j ava.awt.*;

impo rt j ava.awt.event.*;

public class Game extends Framework {

protected void dolnitialization(int width, int height) { initSubmarine(width, height); initBoat(width, height);

initBomb (width,height); }

synchronized public void drawFrame(Graphics g, int width, int height)

{

g.setColor(Color.green); g.fillRect(0,0,width,height) ; doBoatFrame(g, width, height); doSubmarineFrame(g, width, height);

doBombFrame(g, width, height); }

synchronized public void keyPressed(KeyEvent evt) { int code = evt.getKeyCode() ; if (code == KeyEvent.VK_LEFT) {

boatCenterX -= 15; }

else if (code == KeyEvent.VK_RIGHT) {

boatCenterX += 15; }

else if (code == KeyEvent.VK_DOWN) { if (bomblsFalling == false)

bomblsFalling = true; }

}

int bombCenterX;

int bombCenterY;

boolean bomblsFalling;

void initBomb(int width, int height) {

bomblsFalling = false;

bombCenterY = 100; }

void doBombFrame(Graphics g, int width, int height) { if (bomblsFalling) { if (bombCenterY > height) { initBomb(width, height); bombCenterX = boatCenterX;

else if (Math.abs(bombCenterX — subCenterX) <= 36 && Math.abs(bombCenterY – subCenterY) <= 21) { explosionFrameNumber = 1; initBomb(width, height); bombCenterX = boatCenterX;

}

else { bombCenterY += 10; g.setColor(Color.red);

g.fillOval(bombCenterX – 8, bombCenterY – 8, 16, 16);

}

}

else {

bombCenterX = boatCenterX; g.setColor(Color.red);

g.fillOval(bombCenterX – 8, bombCenterY – 8, 16, 16); }

}

int boatCenterX; int boatCenterY;

void initBoat(int width, int height) { boatCenterX = width/2;

boatCenterY = 80; }

void doBoatFrame(Graphics g, int width, int height) { if (boatCenterX < 0)

boatCenterX = 0; else if (boatCenterX > width)

boatCenterX = width; g.setColor(Color.blue);

g.fillRoundRect(boatCenterX – 40, boatCenterY – 20, 80, 40, 20, 20); }

int subCenterX; int subCenterY; boolean sublsMovingLeft; int explosionFrameNumber;

void initSubmarine(int width, int height) { subCenterX = (int)(width * Math.random()); subCenterY = height — 40;

explosionFrameNumber = 0; if (Math.random() < 0.5) sublsMovingLeft = true; else

sublsMovingLeft = false; }

void doSubmarineFrame(Graphics g, int width, int height) { if (explosionFrameNumber >0) { if (explosionFrameNumber ==14) {

initSubmarine(width,height); }

else if (explosionFrameNumber >10) {

explosionFrameNumber++; }

else {

g.setColor(Color.black);

g.fillOval(subCenterX – 30, subCenterY – 15, 60, 30); g.setColor(Color.yellow);

g.fillOval(subCenterX — 4*explosionFrameNumber, subCenterY — 2*explosionFrameNumber, 8*explosionFrameNumber, 4*explosionFrameNumber); g.setColor(Color.red);

g.fillOval(subCenterX — 2*explosionFrameNumber, subCenterY — explosionFrameNumber/2, 4*explosionFrameNumber, explosionFrameNumber);

explosionFrameNumber++; }

}

else {

if (Math, random() < 0.04) sublsMovingLeft = !sublsMovingLeft;

if (sublsMovingLeft) { subCenterX -= 5; if (subCenterX <= 0) { subCenterX = 0; sublsMovingLeft = false;

}

else { subCenterX += 5; if (subCenterX > width) { subCenterX = width;

sublsMovingLeft = true; }

g.setColor(Color.black);

g.fillOval(subCenterX – 30, subCenterY – 15, 60, 30); }

Игра активизируется после щелчка мыши по апплету. Панель с шариком управляется при помощи клавиш стрелок. Шар-снаряд запускается клавишей "стрелка вниз". При попадании она взрывается.

Рис. 1.22. Вращающиеся отрезки

В заключение этого раздела приведем еще один пример (рис. 1.22). В этом апплете показаны короткие отрезки, расположенные равномерно по рядам и столбцам. Один конец каждого отрезка зафиксирован, отрезок может вокруг него вращаться. Изначально все отрезки вращаются случайным образом. Если над апплетом появляется мышь, отрезки прекращают вращение,все отрезки поворачиваются в сторону курсора. Если курсор останавливается, то через некоторое время отрезки вновь начинают хаотическое вращение. Скорость вращения выбирается случайным образом. При нажатии кнопки мыши цвет отрезков меняется. Цвет зависит от положения курсора мыши.

Листинг 1.24. Файл LinesMove.java                                                                                                   j

impo гt j ava.awt.*; import java.applet.*;

public class LinesMove extends Applet implements Runnable {

boolean used = false; // значение переменной used при первой ориентации

//на курсор мыши становится равным true int ROWS = 5;       // количество рядов

int COLUMNS = 7;      // количество столбцов

int[][] angle;        // угол

int[][] driftSpeed; // угловая скорость

long lastTime =0; // время последней инициализации порядка int delayToDrift = 1500; // задержка при упорядочении int width = -1;                      // ширина апплета

int height = -1;      // высота апплета

int hSpace;           // горизонтальное расстояние между линиями

int vSpace;           // вертикальное расстояние между линиями

int space;            // длина каждой линии

int last_x = -1;      // координаты точки, на которую линии были

int last_y = -1;      // ориентированы в прошлый раз

int sleepTime = 100; // задержка между фреймами Thread runner = null; // поток анимации

Image OSC = null;     // невидимый холст для двойной буферизации

Graphics OSCGraphics;

public void init() {

setBackground(Color.black);

setForeground(Color.white);

String param;

param = getParameter("rows");

if (param != null) { try {

ROWS = Integer.parselnt(param) ; }

catch (NumberFormatException e) { } }

param = getParameter("columns"); if (param != null) { try {

COLUMNS = Integer.parselnt(param); }

catch (NumberFormatException e) { } }

angle = new int[ROWS][COLUMNS]; // случайные скорости и углы

driftSpeed = new int[ROWS][COLUMNS];

for (int i = 0; i<ROWS; i++)

for (int j = 0; j<COLUMNS; j++) {

angle[i][j] = (int)(360*Math.random());

driftSpeed [i] [j ] = (int) (4l*Math. random() ) -20; }

}

public void start() { if (runner == null) { int h = size().height; int w = size().width; if (h != height || w != width) doResize(w,h); runner = new Thread(this); runner.start () ;

}

}

public void stop() { // остановка анимации if (runner != null) { runner.stop(); runner = null;

Источник: Будилов В. А. Интернет-программирование на Java. — СПб.: БХВ-Петербург, 2003. — 704 е.: ил.

По теме:

  • Комментарии