4. Обработка событий

От аплетов Java было бы немного толку, если бы они не умели обрабатывать информацию, поступающую от мыши и клавиатуры. К счастью, такая обработка предусмотрена и она выполняется достаточно просто.

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

Если вы создавали приложения для операционной системы Microsoft Windows, здесь для вас нет ничего нового - вспомните, как вы обрабатывали сообщение WM_LBUTTONDOWN или WM_CHAR. Когда пользователь выполнял действие с мышью или клавиатурой в окне приложения, функция этого окна получала соответствующее сообщение. Методы класса Applet, обрабатывающие события от мыши и клавиатуры, являются аналогами обработчиков указанных сообщений.

Заметим, что аплеты имеют дело только с левой клавишей мыши. В текущей версии Java вы не можете никаким образом задействовать в аплете правую или среднюю клавишу мыши.

Как обрабатываются события

Когда возникает событие, управление получает метод handleEvent из класса Component. Класс Applet является дочерним по отношению к классу Component.

Прототип метода handleEvent мы привели ниже:

public boolean handleEvent(Event evt);

В качестве параметра методу handleEvent передается объект класса Event, который содержит всю информацию о событии. По содержимому полей класса Event вы можете определить координаты курсора мыши в момент, когда пользователь нажал клавишу, отличить одинарный щелчок от двойного и так далее.

Ниже мы привели список полей класса Event, которые вы можете проанализировать:

Поле Описание
public Object arg; Произвольный аргумент события, значение которого зависит от типа события
public int clickCount; Это поле имеет значение только для события с типом MOUSE_DOWN и содержит количество нажатий на клавишу мыши. Если пользователь сделал двойной щелчок мышью, в это поле будет записано значение 2
public Event evt; Следующее событие в связанном списке
public int id; Тип события. Ниже мы перечислим возможные значения для этого поля
public int key; Код нажатой клавиши (только для события, созданного при выполнении пользователем операции с клавиатурой)
public int modifiers; Состояние клавиш модификации <Alt>, <Ctrl>, <Shift>
public Object target; Компонент, в котором произошло событие
public long when; Время, когда произошло событие
public int x; Координата по оси X
public int y; Координата по оси Y

Поле id (тип события) может содержать следующие значения:

Значение Тип события
ACTION_EVENT Пользователь хочет, чтобы произошло некоторое событие
GOT_FOCUS Компонент (в нашем случае окно аплета) получил фокус ввода. О фокусе ввода вы узнаете из раздела, посвященного работе с клавиатурой
KEY_ACTION Пользователь нажал клавишу типа “Action”
KEY_ACTION_RELEASE Пользователь отпустил клавишу типа “Action”
KEY_PRESS Пользователь нажал обычную клавишу
KEY_RELEASE Пользователь отпустил обычную клавишу
LIST_DESELECT Отмена выделения элемента в списке
LIST_SELECT Выделение элемента в списке
LOAD_FILE Загрузка файла
LOST_FOCUS Компонент потерял фокус ввода
MOUSE_DOWN Пользователь нажал клавишу мыши
MOUSE_DRAG Пользователь нажал клавишу мыши и начал выполнять перемещение курсора мыши
MOUSE_ENTER Курсор мыши вошел в область окна аплета
MOUSE_EXIT Курсор мыши покинул область окна аплета
MOUSE_MOVE Пользователь начал выполнять перемещение курсора мыши, не нажимая клавишу мыши
MOUSE_UP Пользователь отпустил клавишу мыши
SAVE_FILE Сохранение файла
SCROLL_ABSOLUTE Пользователь переместил движок полосы просмотра в новую позицию
SCROLL_LINE_DOWN Пользователь выполнил над полосой просмотра операцию сдвига на одну строку вниз
SCROLL_LINE_UP Пользователь выполнил над полосой просмотра операцию сдвига на одну строку вверх
SCROLL_PAGE_DOWN Пользователь выполнил над полосой просмотра операцию сдвига на одну страницу вниз
SCROLL_PAGE_UP Пользователь выполнил над полосой просмотра операцию сдвига на одну страницувверх
WINDOW_DEICONIFY Пользователь запросил операцию восстановления нормального размера окна после его минимизации
WINDOW_DESTROY Пользователь собирается удалить окно
WINDOW_EXPOSE Окно будет отображено
WINDOW_ICONIFY Окно будет минимизировано
WINDOW_MOVED Окно будет перемещено

Если событие связано с клавиатурой (тип события KEY_ACTION или KEY_ACTION_RELEASE), в поле key может находиться одно из следующих значений:

Значение Клавиша
DOWN Клавиша перемещения курсора вниз
END <End>
F1 <F1>
F2 <F2>
F3 <F3>
F4 <F4>
F5 <F5>
F6 <F6>
F7 <F7>
F8 <F8>
F9 <F9>
F10 <F10>
F11 <F11>
F12 <F12>
HOME <Home>
LEFT Клавиша перемещения курсора влево
PGDN <Page Down>
PGUP <Page Up>
RIGHT Клавиша перемещения курсора вправо
UP Клавиша перемещения курсора вниз

Могут быть указаны следующие маски для поля модификаторов modifiers:

Значение маски Описание
ALT_MASK Была нажата клавиша <Alt>
META_MASK Была нажата мета-клавиша (клавиша для ввода диактрических символов)
CTRL_MASK Была нажата клавиша <Ctrl>
SHIFT_MASK Была нажата клавиша <Shift>

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

События от мыши

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

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

Программисты, создававшие приложения для операционной системы Microsoft Windows, могут найти здесь аналогию с вызовом функции DefWindowProc, которая выполняет обработку сообщений, принятую по умолчанию.

Нажатие клавиши мыши

Переопределив метод mouseDown, вы сможете отслеживать нажатия клавиши мыши. Прототип этого метода приведен ниже:

public boolean mouseDown XE "mouseDown" (Event evt, int x, int y);

Через параметр evt методу передается ссылка на объект Event, с помощью которой метод может получить полную информацию о событии.

Анализируя содержимое параметров x и y, приложение может определить координаты курсора на момент возникновения события.

Заметим, что для отслеживания двойного щелчка мыши не предусмотрено никакого отдельного метода. Однако анализируя содержимое поля clickCount переменной evt, вы можете определить кратность щелчка мыши:

if(evt.clickCount > 1)
  // Двойной щелчок
  showStatus("Mouse Double Click");
else
  // Одинарный щелчок
  showStatus("Mouse Down");

Отпускание клавиши мыши

При отпускании клавиши мыши управление получает метод mouseUp:

public boolean mouseUp(Event evt, int x, int y);

Анализируя параметры x и y, вы можете определить координаты точки, в которой пользователь отпустил клавишу мыши.

Перемещение курсора мыши

Когда пользователь перемещает курсор мыши над окном аплета, в процессе перемещения происходит вызов метода mouseMove:

public boolean mouseMove(Event evt, int x, int y);

Через переменные x и y передаются текущие координаты курсора мыши.

Выполнение операции Drag and Drop

Операция Drag and Drop выполняется следующим образом: пользователь нажимает клавишу мыши и, не отпуская ее, начинает перемещать курсор мыши. При этом происходит вызов метода mouseDrag:

public boolean mouseDrag(Event evt, int x, int y);

Через переменные x и y передаются текущие координаты курсора мыши. Метод mouseDrag вызывается даже в том случае, если в процессе перемещения курсор вышел за пределы окна аплета.

Вход курсора мыши в область окна аплета

Метод mouseEnter получает управление, когда курсор мыши в процессе перемещения по экрану попадает в область окна аплета:

public boolean mouseEnter(Event evt, int x, int y);

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

Выход курсора мыши из области окна аплета

Метод mouseExit вызывается при покидании куросром окна аплета:

public boolean mouseExit(Event evt, int x, int y);

Если пользователь убрал курсор из окна аплета, активизированного методом mouseEnter, то метод mouseExit может переключить аплет в пассивное состояние.

Приложение MouseClick

Аплет MouseClick демонстрирует обработку событий, поступающих от мыши.

Когда мы создавали проект этого аплета, то в третьей диалоговой панели системы Java Applet Wizard включили три переключателя в поле Which mouse event handlers would you like added (рис. 4.1).

Рис. 4.1. Включение обработчиков событий от мыши

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

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

Действие пользователя Реакция аплета
Перемещение курсора мыши при отжатой клавише Игнорирование
Перемещение курсора мыши при нажатой клавише В строку состояния записывается текстовая строка Mouse Drag
Нажатие клавиши мыши В месте расположения курсора выводятся текущие координаты курсора мыши. Дополнительно в строку состояния записывается текстовая строка Mouse Down
Отжатие клавиши мыши В строку состояния записывается текстовая строка Mouse Up
Курсор мыши входит в область окна аплета В строку состояния записывается текстовая строка Mouse pointer enters applet's window
Курсор мыши выходит из области окна аплета В строку состояния записывается текстовая строка Mouse pointer leaves applet's window

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

Рис. 4.2. Внешний вид окна аплета MouseClick, в котором отображаются координаты курсора

Исходные тексты приложения

Файл исходного текста приложения MouseClick представлен в листинге 4.1.

Листинг 4.1. Файл MouseClick\MouseClick.java

// =========================================================
// Обработка событий от мыши
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;

public class MouseClick extends Applet
{
  // Текущие координаты курсора при нажатии на 
  // кнопку мыши
  Dimension dimMouseCursor;

  // Временная переменная для хранения события
  Event ev;

  // -------------------------------------------------------
  // getAppletInfo
  // Метод, возвращающей строку информации об аплете
  // -------------------------------------------------------
  public String getAppletInfo()
  {
    return "Name: MouseClick\r\n" +
      "E-mail: frolov@glas.apc.org" +
      "WWW:    http://www.glasnet.ru/~frolov" +
      "Author: Alexandr Frolov\r\n" +
      "Created with Microsoft Visual J++ Version 1.0";
  }

  // -------------------------------------------------------
  // paint
  // Метод paint, выполняющий рисование в окне аплета
  // -------------------------------------------------------
  public void paint(Graphics g)
  {
    // Определяем текущие размеры окна аплета
    Dimension dimAppWndDimension = size();
    
    // Выбираем в контекст отображения желтый цвет
    g.setColor(Color.yellow);
    
    // Закрашиваем внутреннюю область окна аплета
    g.fillRect(0, 0, 
      dimAppWndDimension.width  - 1, 
      dimAppWndDimension.height - 1);

    // Выбираем в контекст отображения черный цвет
    g.setColor(Color.black);

    // Рисуем рамку вокруг окна аплета
    g.drawRect(0, 0, 
      dimAppWndDimension.width  - 1, 
      dimAppWndDimension.height - 1);

    // Отображаем текущие координаты курсора мыши
    // в точке, где находится этот курсор
    g.drawString("(" + ev.x + "," + ev.y + ")", ev.x, ev.y);
  }

  // -------------------------------------------------------
  // mouseDown
  // Обработка щелчка кнопкой мыши
  // -------------------------------------------------------
  public boolean mouseDown(Event evt, int x, int y)
  {
    // Определяем и сохраняем текущие координаты
    // курсора мыши
    dimMouseCursor = new Dimension(x, y);
    
    // Сохраняем событие во временной переменной
    ev = evt;

    // Если количествао щелчков больше 1, считаем что
    // сделан двойной щелчок
    if(evt.clickCount > 1)
      
      // Выводим сообщение о двойном щелчке
      showStatus("Mouse Double Click");

    // Сделан одиночный щелчок
    else

      // Выводим сообщение о простом щелчке
      showStatus("Mouse Down");
    
    // Перерисовываем окно аплета
    repaint();

    // Возвращаем значение true при успешной
    // обработке события
    return true;
  }

  // -------------------------------------------------------
  // mouseUp
  // Отпускание клавиши мыши
  // -------------------------------------------------------
  public boolean mouseUp(Event evt, int x, int y)
  {
    // Выводим сообщение в строке состояния
    showStatus("Mouse Up");
      return true;
  }

  // -------------------------------------------------------
  // mouseDrag
  // Перемещение курсора мыши при нажатой клавише
  // -------------------------------------------------------
  public boolean mouseDrag(Event evt, int x, int y)
  {
    // Выводим сообщение в строке состояния
    showStatus("Mouse Drag");
    return true;
  }

  // -------------------------------------------------------
  // mouseMove
  // Перемещение курсора мыши при отжатой клавише
  // -------------------------------------------------------
  public boolean mouseMove(Event evt, int x, int y)
  {
    return true;
  }

  // -------------------------------------------------------
  // mouseEnter
  // Курсор мыши вошел в область окна аплета
  // -------------------------------------------------------
  public boolean mouseEnter(Event evt, int x, int y)
  {
    // Выводим сообщение в строке состояния
    showStatus("Mouse pointer enters applet's window");
    return true;
  }

  // -------------------------------------------------------
  // mouseExit
  // Курсор мыши покинул область окна аплета
  // -------------------------------------------------------
  public boolean mouseExit(Event evt, int x, int y)
  {
    // Выводим сообщение в строке состояния
    showStatus("Mouse pointer leaves applet's window");
    return true;
  }
}

Исходный текст документа HTML, созданного для нашего аплета системой Java Applet Wizard, приведен в листинге 4.2.

Листинг 4.2. Файл MouseClick\MouseClick.html

<html>
<head>
<title>MouseClick</title>
</head>
<body>
<hr>
<applet
    code=MouseClick.class
    id=MouseClick
    width=320
    height=240 >
</applet>
<hr>
<a href="MouseClick.java">The source.</a>
</body>
</html>

Описание исходного текста

В исходном тексте класса MouseClick мы определили поля класса с именами dimMouseCursor и ev:

Dimension dimMouseCursor;
Event ev;

Первое из них предназначено для хранения координат курсора в момент возникновения события, а второе - хранит ссылку на это событие.

Метод getAppletInfo

Метод getAppletInfo ничем не отличается от аналогичных методов в предыдущих приложениях.

Метод paint

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

Далее метод paint отображает текущие координаты курсора мыши, взяв их из переменной ev:

g.drawString("(" + ev.x + "," + ev.y + ")", ev.x, ev.y);

Метод mouseDown

Когда пользователь делает щелчок левой клавишей мыши (напомним, что Java не работает с другими клавишами мыши), управление получает метод mouseDown.

Этот метод, переопределенный в нашем приложении, прежде всего сохраняет текущие координаты курсора мыши в переменной dimMouseCursor класса Dimension:

dimMouseCursor = new Dimension(x, y);

Событие, которое передается методу mouseDown через первый параметр, сохраняется в переменной ev:

ev = evt;

Далее метод mouseDown проверяет поле clickCount параметра evt:

if(evt.clickCount > 1)
  showStatus("Mouse Double Click");
else
  showStatus("Mouse Down");

В это поле записывается кратность щелчка мыши. Если пользователь сделал двойной щелчок, в строке состояния отображается текстовая строка Mouse Double Click, а если одинарный - строка Mouse Down.

Обратите внимание на метод showStatus. Этот метод позволяет аплету отобразить любую текстовую строку в строке состояния навигатора, поэтому он часто используется для отладки или выдачи текущей информации о состоянии аплета.

Заметим, однако, что в документе HTML может располагаться несколько разных аплетов, а строка состояния навигатора только одна. Поэтому сообщения от разных аплетов могут перекрывать друг друга, в результате чего в строке состояния появится только то сообщение, которое было записано туда последним.

После записи сообщения в строку состояния метод mouseDown перерисывывает окно аплета, вызывая для этого метод repaint:

repaint();

В результате вызова метода repaint происходит инициирование вызова метода paint, выполняющего перерисовку содержимого окна аплета. Однако не следует думать, будто метод repaint просто вызывает метод paint. Метод paint вызывается интерпретатором Java асинхронно по отношению к методу repaint в подходящий момент времени.

В последней строке метод mouseDown возвращает значение true:

return true;

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

Методы mouseUp, mouseDrag, mouseEnter, mouseExit

Обработчики методов mouseUp, mouseDrag, mouseEnter и mouseExit выглядят одинаково:

public boolean mouseUp(Event evt, int x, int y)
{
  // Выводим сообщение в строке состояния
  showStatus("Mouse Up");
    return true;
}

Пользуясь методом showStatus, эти методы записывают соответствующее сообщение в строку состояния и возвращают значение true.

Метод mouseMove

Метод mouseMove выглядит следующим образом:

public boolean mouseMove(Event evt, int x, int y)
{
  return true;
}

Он ничего не делает, кроме того что возвращает значение true, блокируя обработку события, выполняемую в базовом классе.

Приложение LineDraw

Следующее приложение тоже работает с мышью. В его окне вы можете рисовать прямые линии черного цвета (рис. 4.3).

Рис. 4.3. Аплет, в окне которого можно рисовать прямые линии

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

По мере того как вы будете рисовать линии, аплет будет заполнять массив с координатами линий. Каждый раз, когда окно аплета будет перерисовываться, метод paint перерисует все линии заново, пользуясь координатами, сохраненными в массиве.

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

Исходные тексты приложения

Исходный текст аплета LineDraw приведен в листинге 4.3.

Листинг 4.3. Файл LineDraw\LineDraw.java

// =========================================================
// Рисование прямых линий
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;

// Добавляем для класса Vector
import java.util.*;

public class LineDraw extends Applet
{
  // Координаты курсора при нажатии кнопки мыши
  Dimension dmDown;

  // Координаты курсора при отжатии кнопки мыши
  Dimension dmUp;

  // Предыдущие координаты конца линии
  Dimension dmPrev;
  
  // Признак включения режима рисования
  boolean bDrawing;

  // Массив координат линий
  Vector lines;

  // -------------------------------------------------------
  // getAppletInfo
  // Метод, возвращающей строку информации об аплете
  // -------------------------------------------------------
  public String getAppletInfo()
  {
    return "Name: LineDraw\r\n" +
      "E-mail: frolov@glas.apc.org" +
      "WWW:    http://www.glasnet.ru/~frolov" +
      "Author: Alexandr Frolov\r\n" +
      "Created with Microsoft Visual J++ Version 1.0";
  }

  // -------------------------------------------------------
  // init
  // Метод, получающий управление при инициализации аплета
  // -------------------------------------------------------
  public void init()
  {
    // Сброс признака рисования
    bDrawing = false;

    // Создание массива для хранения координат линий
    lines = new Vector();
  }

  // -------------------------------------------------------
  // paint
  // Метод paint, выполняющий рисование в окне аплета
  // -------------------------------------------------------
  public void paint(Graphics g)
  {
    // Определяем текущие размеры окна аплета
    Dimension dimAppWndDimension = size();
    
    // Выбираем в контекст отображения желтый цвет
    g.setColor(Color.yellow);
    
    // Закрашиваем внутреннюю область окна аплета
    g.fillRect(0, 0, 
      dimAppWndDimension.width  - 1, 
      dimAppWndDimension.height - 1);

    // Выбираем в контекст отображения черный цвет
    g.setColor(Color.black);

    // Рисуем рамку вокруг окна аплета
    g.drawRect(0, 0, 
      dimAppWndDimension.width  - 1, 
      dimAppWndDimension.height - 1);
    
    // Рисуем в цикле линии, координаты которых 
    // хранятся в массиве lines
    for (int i=0; i < lines.size(); i++) 
    {
      // Получаем координаты очередной линии
      Rectangle p = (Rectangle)lines.elementAt(i);

      // Рисуем линию
      g.drawLine( p.width, p.height, p.x, p.y);
    }
   
    // Сбрасываем режим рисования
    bDrawing = false;
  }

  // -------------------------------------------------------
  // mouseDown
  // Обработка щелчка кнопкой мыши
  // -------------------------------------------------------
  public boolean mouseDown(Event evt, int x, int y)
  {
    // Если количествао щелчков больше 1, считаем что
    // сделан двойной щелчок
    if(evt.clickCount > 1)
    {
      // Удаляем все строки из массива lines
      lines.removeAllElements();

      // Перерисовываем окно аплета
      repaint();

      return true;
    }

    // Сохраняем текущие координаты начала линии
    dmDown = new Dimension(x, y);

    // В начале процесса рисования линии устанавливаем
    // предыдущие координаты конца линии равными
    // координатам начала линии
    dmPrev = new Dimension(x, y);

    // Отключаем режим рисования
    bDrawing = false;
		return true;
  }

  // -------------------------------------------------------
  // mouseUp
  // Отпускание клавиши мыши
  // -------------------------------------------------------
  public boolean mouseUp(Event evt, int x, int y)
  {
    // Проверяем, включен ли режим рисования
    if(bDrawing)
    {
      // Если режим рисования включен, добавляем
      // новый элемент в массив lines
      
      // Сохраняем координаты конца линии
      dmUp = new Dimension(x, y);
      
      // Добавляем линию в массив
      lines.addElement(
        new Rectangle(dmDown.width, dmDown.height, x, y));
      
      // Перерисовываем окно аплета
      repaint();

      // Отключаем режим рисования
      bDrawing = false;
    }
    return true;
  }

  // -------------------------------------------------------
  // mouseDrag
  // Перемещение курсора мыши при нажатой клавише
  // -------------------------------------------------------
  public boolean mouseDrag(Event evt, int x, int y)
  {
    // Получаем контекст отображения для окна аплета
    Graphics g = getGraphics();

    // Включаем режим рисования
    bDrawing = true;

    // Закрашиваем предыдущую линию цветом фона
    // (то есть стираем ее)
    g.setColor(Color.yellow);

    g.drawLine(dmDown.width, dmDown.height, 
      dmPrev.width, dmPrev.height);

    // Рисуем новую линию черным цветом
    g.setColor(Color.black);
    g.drawLine(dmDown.width, dmDown.height, x, y);

    // Сохраняем координаты предыдущей линии,
    // чтобы стереть ее в следующий раз
    dmPrev = new Dimension(x, y);
    return true;
  }

  // -------------------------------------------------------
  // mouseMove
  // Перемещение курсора мыши при отжатой клавише
  // -------------------------------------------------------
  public boolean mouseMove(Event evt, int x, int y)
  {
    // Отключаем режим рисования
    bDrawing = false;
    return true;
  }
}

Исходный текст документа HTML, созданного для аплета LineDraw, вы найдете в листинге 4.4.

Листинг 4.4. Файл LineDraw\LineDraw.html

<html>
<head>
<title>LineDraw</title>
</head>
<body>
<hr>
<applet
    code=LineDraw.class
    id=LineDraw
    width=320
    height=240 >
</applet>
<hr>
<a href="LineDraw.java">The source.</a>
</body>
</html>

Описание исходного текста

В нашем аплете мы будем создавать объект класса Vector, который является массивом с динамически изменяемым размером. Этот класс имеет полное имя java.util.Vector, поэтому мы подключаем соответствующую библиотеку классов:

import java.util.*;

Поля класса LineDraw

В нашем классе мы определили несколько полей, предназначенных для хранения текущих координат рисуемых линий.

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

Когда пользователь отпускает клавишу мыши, координаты записываются в переменную dmUp.

В процессе рисования линии метод mouseDrag стирает ранее нарисованную линию и рисует новую. Координаты конца старой линии хранятся в переменной dmPrev.

Переменная bDrawing типа boolean хранит текущее состояние аплета. Когда аплет находится в состоянии рисования линии, в эту переменную записывается значение true, а когда нет - значение false.

И, наконец, переменная lines типа Vector является динамическим массивом, в котором хранятся координаты нарисованных линий.

Метод getAppletInfo

Метод getAppletInfo возвращает информацию об аплете и не имеет никаких особенностей.

Метод init

Метод init сбрасывает признак рисования, записывая в поле bDrawing значение false, а также создает новый динамический массив в виде объекта класса Vector:

public void init()
{
  bDrawing = false;
  lines = new Vector();
}

Метод paint

После обычной для наших аплетов раскраски фона и рисования рамки метод paint перебирает в цикле все элементы массива lines, рисуя линии:

for (int i=0; i < lines.size(); i++) 
{
  Rectangle p = (Rectangle)lines.elementAt(i);
  g.drawLine(p.width, p.height, p.x, p.y);
}

Для объектов класса Vector можно использовать метода size, возвращающий количество элементов в массиве, чем мы воспользовались для проверки условия выхода из цикла.

Чтобы извлечь элемент массива по его номеру, мы воспользовались методом elementAt, передав ему через единственный параметр номер извлекаемого элемента.

Так как в массиве хранятся объекты класса Rectangle, перед инициализацией ссылки p мы выполняем преобразование типов.

Перед завершением работы метод paint сбрасывает признак рисования, записывая в поле bDrawing значение false:

bDrawing = false;

Метод mouseDown

В начале своей работы метод mouseDown определяет, был ли сделан одинарный щелчок клавишей мыши, или двойной. Если был сделан двойной щелчок мышью, метод удаляет все элементы из массива list, а затем перерисовывает окно аплета, вызывая метод repaint:

lines.removeAllElements();
repaint();

После перерисовки окно аплета очищается от линий.

Если же был сделан одинарный щелчок клавишей мыши, метод mouseDown сохраняет текущие координаты курсора в переменных dmDown и dmPrev, а затем сбрасывает признак рисования:

dmDown = new Dimension(x, y);
dmPrev = new Dimension(x, y);
bDrawing = false;

Метод mouseUp

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

dmUp = new Dimension(x, y);
lines.addElement(
  new Rectangle(dmDown.width, dmDown.height, x, y));
repaint();

После добавления элемента в массив метод mouseUp инициирует перерисовку окна аплета, вызывая для этого метод repaint.

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

Метод mouseDrag

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

Для того чтобы нарисовать что-либо в окне аплета, наобходимо получить контекст отображения. Методу paint этот контекст передается через парметр как объект класса Graphics. Если же вы собираетесь рисовать в другом методе, отличном от paint, необходимо получить контекст отображения, например, так:

Graphics g = getGraphics();

После получения контекста отображения и включения режима рисования (записью в переменную bDrawing значения true) метод mouseDrag стирает линию, которая была нарисована ранее, в процессе предыдущего вызова этого же метода:

g.setColor(Color.yellow);
g.drawLine(dmDown.width, dmDown.height, 
   dmPrev.width, dmPrev.height);

Для стирания линии мы рисуем ее на том же месте с использованием цвета, совпадающего с цветом фона.

Далее метод mouseDrag рисует новую линию черного цвета, соединяя точку, в которой была нажата клавиша мыши, с точкой текущего расположения курсора мыши:

g.setColor(Color.black);
g.drawLine(dmDown.width, dmDown.height, x, y);

После рисования линии координаты ее конца сохраняются в поле dmPrev для стирания этой линии при следующем вызове метода mouseDrag:

dmPrev = new Dimension(x, y);
return true;

Метод mouseMove

Метод mouseMove не делает ничего, за исключением того, что он отключает режим рисования. Таким образом, простое перемещение курсора мыши над окном аплета не приводит к рисованию линий.

События от клавиатуры

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

Для того чтобы обработать события от клавиатуры, ваш аплет должен переопределить методы keyDown и keyUp:

public boolean keyDown(Event evt, int nKey) 
{
  . . .
}
public boolean keyUp(Event evt, int nKey) 
{
  . . .
}

В качестве первого параметра этим методам передается объект типа Event, о полях которого мы рассказывали в разделе “Как обрабатываются события” этой главы.

Наибольший интерес представляют поля объекта evt с именами key и modifiers. Через них передается, соответственно, код нажатой клавиши и код модификации. Возможные значения для этих полей вы найдете в только что указанном разделе.

Второй параметр методов keyDown и keyUp с именем nKey дублирует поле key объекта evt.

Приложение KeyCode

Для демонстрации методов обработки клавиатурных событий мы подготовили аплет KeyCode. В его окне отображаются символы, соответствующие нажимаемым клавишам, код соответствующих клавиш и коды модификации (рис. 4.4).

Рис. 4.4. Окно аплета KeyCode

Аплет KeyCode интересен тем, что в процессе отображения новые строки появляются в верхней части окна, а старые сдвигаются вниз после отпускания клавиши. Таким образом, мы организовали свертку в окне аплета.

Прежде чем нажимать клавиши, вы должны передать фокус вода окну аплета. Это можно сделать, щелкнув в окне левой клавишей мыши. Фокус ввода - это атрибут, который присваивается окну, обрабатывающему ввод от клавиатуры. Так как клавиатура одна, а аплетов и других активных окон на экране может быть много, необходим механизм, позволяющий определить, в какое окно направялются события, создаваемые клавиатурой. Такой механизм и обеспечивается атрибутом фокуса ввода.

Исходные тексты приложения KeyCode

Файл исходного текста приложения KeyCode приведен в листинге 4.5.

Листинг 4.5. Файл KeyCode\KeyCode.java

// =========================================================
// Просмотр кодов клавиш
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;

public class KeyCode extends Applet
{
  // Высота символов текущего шрифта
  int yHeight;

  // Текущие размеры окна аплета
  Dimension dimAppWndDimension;

  // -------------------------------------------------------
  // getAppletInfo
  // Метод, возвращающей строку информации об аплете
  // -------------------------------------------------------
  public String getAppletInfo()
  {
    return "Name: KeyCode\r\n" +
	"Author: Alexandr Frolov\r\n" +
       "WWW:    http://www.glasnet.ru/~frolov" +
       "Author: Alexandr Frolov\r\n" +
	"Created with Microsoft Visual J++ Version 1.0";
  }

  // -------------------------------------------------------
  // init
  // Метод, получающий управление при инициализации аплета
  // -------------------------------------------------------
  public void init()
  {
    // Получаем контекст отображения
    Graphics g = getGraphics();

    // Определяем метрики текущего шрифта
    FontMetrics fm = g.getFontMetrics();

    // Сохраняем полную высоту символов шрифта
    yHeight = fm.getHeight();
  }

  // -------------------------------------------------------
  // paint
  // Метод paint, выполняющий рисование в окне аплета
  // -------------------------------------------------------
  public void paint(Graphics g)
  {
    // Определяем текущие размеры окна аплета
    dimAppWndDimension = size();
    
    // Выбираем в контекст отображения желтый цвет
    g.setColor(Color.yellow);
    
    // Закрашиваем внутреннюю область окна аплета
    g.fillRect(0, 0, 
      dimAppWndDimension.width  - 1, 
      dimAppWndDimension.height - 1);

    // Выбираем в контекст отображения черный цвет
    g.setColor(Color.black);

    // Рисуем рамку вокруг окна аплета
    g.drawRect(0, 0, 
      dimAppWndDimension.width  - 1, 
      dimAppWndDimension.height - 1);
  }

  // -------------------------------------------------------
  // keyDown
  // Вызывается, когда пользователь нажимает на клавишу
  // -------------------------------------------------------
  public boolean keyDown(Event evt, int nKey) 
  {
    // Массив для преобразования кода символа в сивол
    char[] chKey;
    
    // Временная строка
    String s;

    // Создаем массив из одного элемента
    chKey = new char[1];

    // Записыаем в него код нажатой клавиши
    chKey[0] = (char)nKey;
    
    // Преобразуем в строку
    s = new String(chKey);
    
    // Получаем контекст отображения
    Graphics g = getGraphics();
    
    // Выбираем черный цвет для рисования
    g.setColor(Color.black);
    
    // Рисуем символ, соответствующий нажатой клавише
    g.drawString(s + " ", 10, 10);

    // Рисуем код клавиши
    g.drawString(
      " -> key: " + evt.key, 20, 10);

    // Рисуем код модификации
    g.drawString(" mod: " + evt.modifiers, 100, 10);

    return true;
  }

  // -------------------------------------------------------
  // keyUp
  // Вызывается, когда пользователь отпускает клавишу
  // -------------------------------------------------------
  public boolean keyUp(Event evt, int nKey) 
  {
    // Получаем контекст отображения
    Graphics g = getGraphics();

    // Выполняем свертку нижней области окна
    g.copyArea(0, 1, 
      dimAppWndDimension.width  - 1, 
      dimAppWndDimension.height - yHeight - 5,
      0, yHeight + 1);

    // Закрашиваем область ввода желтым цветом
    g.setColor(Color.yellow);

    g.fillRect(1, 1, 
      dimAppWndDimension.width  - 2, yHeight + 1);

    return true;
  }
}

В листинге 4.6 вы найдете исходный текст документа HTML, в который встроен наш аплет.

Листинг 4.6. Файл KeyCode\KeyCode.html

<html>
<head>
<title>KeyCode</title>
</head>
<body>
<hr>
<applet
    code=KeyCode.class
    id=KeyCode
    width=320
    height=240 >
</applet>
<hr>
<a href="KeyCode.java">The source.</a>
</body>
</html>

Описание исходного текста

Наш аплет определяет несколько полей в своем классе и переопределяет несколько методов базового класса.

Поля класса KeyCode

Поле yHeight используется для хранения полной высоты символов текущего шрифта, выбранного в контекст отображения окна аплета. Эта величина нужна для определения шага свертки окна.

В поле dimAppWndDimension типа Dimension хранятся текущие размеры окна аплета.

Метод getAppletInfo

Метод getAppletInfo возвращает информацию об аплете и не имеет никаких особенностей.

Метод init

Этот метод получает контекст отображения, однако не для рисования, а для определения метрик шрифта:

Graphics g = getGraphics();
FontMetrics fm = g.getFontMetrics();

В переменную yHeight заносится полная высота символов текущего шрифта:

yHeight = fm.getHeight();

Метод paint

Метод paint закрашивает окно аплета желтым цветом и обводит его рамкой. Никакой другой работы этот метод не выполняет.

Метод keyDown

Когда пользователь нажимает клавишу, управление передается методу keyDown. Обработчик метода keyDown преобразует код нажатой клавиши nKey в текстовую строку типа String и затем отображает эту строку и содержимое двух полей объекта evt в окне аплета.

Преобразование выполняется в два приема.

Вначале код символа, имеющий тип int, преобразуется к типу char и записывается в ячейку массива типа char[], как это показано ниже:

char[] chKey;
String s;
chKey = new char[1];
chKey[0] = (char)nKey;

Затем этот массив, состоящий только из одного элемента, преобразуется в текстовую строку:

s = new String(chKey);

Далее метод ketDown получает контекст отображения, устанавливает в нем черный цвет и рисует в верхней части окна параметры клавиатурного события:

Graphics g = getGraphics();
g.setColor(Color.black);
g.drawString(s + " ", 10, 10);
g.drawString(" -> key: " + evt.key, 20, 10);
g.drawString(" mod: " + evt.modifiers, 100, 10);

Метод keyUp

Метод keyUp получает управление, когда пользователь отпускает ранее нажатую клавишу. Ему передаются те же параметры, что и только что рассмотренному методу keyDown.

Наш метод keyUp получает контекст отображения, а затем выполняет копирование верхней части окна, сдвигая ее вниз на размер символов текущего шрифта:

g.copyArea(0, 1, 
  dimAppWndDimension.width  - 1, 
  dimAppWndDimension.height - yHeight - 5,
  0, yHeight + 1);

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

g.setColor(Color.yellow);
g.fillRect(1, 1, 
  dimAppWndDimension.width  - 2, yHeight + 1);