Пять уроков по Java

        

За работу!


Самое время написать какой-нибудь аплет. Я пользуюсь в своей работе набором Java Developer Kit 1.1 (JDK). И не потому, что это удобно. Как раз наоборот: Symantec Visual Cafe или Microsoft Visual J++ гораздо лучше подходят для работы. Зато JDK - это стандартное средство, и к тому же его можно получить совершенно бесплатно с сервера http://java.sun.com.

Чтобы разобраться с тем, как сообщения курсируют по программе, напишем такой аплет, который будет говорить нам обо всех происходящих событиях. Для показа сообщений будем использовать стандартный поток вывода, т. е. экран монитора. Вывод сообщений будет производиться вызовами функций вывода System. out.println(), которые часто используются для трассировки различных данных во время отладки. System. out.println() показывает на экране заданную вами строку и переводит курсор на следующую строку. Если перевод курсора не требуется, то можно воспользоваться функцией System.out.print().

Наберите следующий исходный текст в файл с именем Events.java:

import java.applet.*; // Импортировать данные для класса Applet import java.awt.*; // Импортировать данные для классов // визуальных элементов import java.io.*; // Нужно для вызовов // System.out.println // Создание класса аплета public class Events extends Applet { Button button; MyTextField text;

public Events() // Конструктор класса // Events { // Вывести сообщение на дисплей System.out.println("--> Constructor called..."); // Создать объект "кнопка" // с надписью "Button" button = new Button("Button"); // Создать объект // "строка редактирования" text = new TextField(); // Добавить компоненты в окно аплета add(button); add(text); // Проверка правильности // расположения компонентов validate(); }

// Вызывается при начальной // инициализации аплета public void init() { System.out.println("--> init() called..."); // this.requestFocus(); }

// Обработчик перерисовки public void paint(Graphics g) { System.out.println("--> paint() called..."); // Разместить кнопку с координата- // ми (10,10) и сделать ее размером // 100x20 button.reshape(10, 10, // 100, 20); разместить строку // ввода с координатами (10,40) // и сделать ее размером 100x20 text.reshape(10, 40, 100, 20); }


public boolean mouseDown( Event evt, int x, int y) { System.out.println("--> mouseDown() called..."); return true; }

public boolean mouseUp(Event evt, int x, int y) { System.out.println("--> mouseUp() called..."); return true; }

public boolean mouseDrag(Event evt, int x, int y) { System.out.println("--> mouseDrag() called..."); return true; }

public boolean mouseMove(Event evt, int x, int y) { System.out.println("--> mouseMove() called..."); return true; }

public boolean mouseEnter(Event evt, int x, int y) { System.out.println("--> mouseEnter() called..."); return true; }



public boolean mouseExit(Event evt, int x, int y) { System.out.println("--> mouseExit() called..."); return true; }

public boolean keyDown(Event evt, int key) { System.out.println("--> keyDown() called..."); return true; }

public boolean keyUp(Event evt, int key) { System.out.println("--> keyUp() called..."); return true; }

public boolean gotFocus(Event evt, Object o) { System.out.println("--> gotFocus() called..."); return true; }

public boolean lostFocus(Event evt, Object o) { System.out.println("--> lostFocus() called..."); return true; }

public boolean action(Event evt, Object o) { System.out.println("--> action() called..."); return true; } }

Если вы будете использовать JDK, то скомпилируйте исходный текст командой

javac.exe Events.java

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

Следующий ход - создание Web-страницы со ссылкой на полученный аплет. Сделаем файл Events.html и наберем следующий исходный текст на языке HTML:

<html> <head> <title>Events</title> </head> <body> <applet code=Events.class width=150 height=200 > </applet> </body> </html>

Все готово? Тогда запускаем наш аплет:

appletviewer.exe Events.html



Поэкспериментируйте, нажимая кнопки мыши и клавиатуры. Посмотрите в DOS-окно на протокол, полученный в результате нажатий. Обратите внимание на некоторые странности в поведении нашего аплета. Во-первых, при попадании указателя мыши на элементы управления возникает событие MOUSE_EXIT, а при переводе указателя назад в окно аплета следует событие MOUSE_ENTER.

Вывод: области, занятые элементами интерфейса, исключаются из окна аплета, и события от мыши в этих областях не обрабатываются.

Во-вторых, не происходит ни одного вызова обработчиков нажатий и отпусканий клавиш клавиатуры. Это говорит о том, что аплет не имеет фокуса ввода. Нет фокуса и у элементов интерфейса. Если нажать клавишу <Tab>, то передачи фокуса не происходит.

Вывод: после создания аплета фокуса ввода нет ни у одного элемента интерфейса.

Если вы хотите, чтобы нажатия клавиш отслеживались аплетом, вам необходимо сделать так, чтобы аплет получил фокус. На самом деле такая строка уже есть:

// this.requestFocus();

Все, что от вас требуется, это убрать признак комментария - две косые черты перед строкой, и перекомпилировать проект. Если теперь запустить аплет и начать нажимать кнопки клавиатуры, то вы увидите, что обработчики нажатий и отпусканий клавиш начали вызываться. Если нажать клавишу <Tab>, фокус ввода перейдет к кнопке. При этом вы увидите, что вызываются обработчики lostFocus() и gotFocus(), отмечающие потерю фокуса ввода одним элементом и получение его другим.

Кстати говоря, мы с вами еще не открыли тайну обработчика action(), а ведь он один из основных. Он вызывается в том случае, когда происходит главное функциональное событие элемента интерфейса. Для классов Button и Checkbox это будет щелчок мыши, для классов Choice, List и MenuItem - выбор элемента, для класса TextField - нажатие клавиши Return. В большинстве случаев вам как программисту будет достаточно именно этого обработчика при решении практически всех задач. Для того чтобы узнать, какой именно элемент интерфейса вызвал обработчик action(), нужно проверить параметр target переданного класса Event. Это ссылка на сработавший объект. Проверить правильность этого факта можно, добавив в обработчик action() пару строчек:



if(evt.target==button) System.out.println("--> Button pressed"); if(evt.target==text) System.out.println("--> Text edited");

Запустите перекомпилированный аплет, понажимайте на кнопку и понабирайте текст в строке ввода, завершая набор нажатием клавиши Return. Сообщения не замедлят появиться в DOS-окне.

Основная работа, связанная с обработкой сообщений аплета, приходится на метод handleEvent(). Для того чтобы убедиться в этом, напишем свой обработчик сообщений handleEvent():

public boolean handleEvent(Event evt) { System.out.println ("-- > handleEvent() called_"); return false; }

Последняя строка возвращает константу false, говорящую о том, что сообщение не обработано. Откомпилируем и запустим модифицированный вариант аплета, наблюдая за диагностическими сообщениями в окне DOS. Как вы можете видеть, теперь весь процесс обработки сообщений сводится к вызовам метода handleEvent(), после которого ничего не происходит. Изменим строку

return false

на вызов метода handleEvent() класса Applet, который является предком для нашего аплета:

return super.handleEvent(evt);

Напомню, что ключевое слово super обозначает ссылку на непосредственного предка класса. В принципе это эквивалентно следующей строке:

return Applet.handleEvent(evt);

К сожалению, компилятор Java выдаст на это выражение сообщение об ошибке, отказываясь делать статическую ссылку на метод объекта. Поэтому все равно придется использовать слово super.

После того как мы изменили содержимое метода handleEvent(), вызовы обработчиков элементарных сообщений (например, mouseMove() и т. п.) снова посыпались как из рога изобилия. Также восстановился поток вызовов метода action(). Обратите внимание, что каждому вызову обработчика элементарного сообщения предшествует вызов метода handleEvent().

Вывод: любое сообщение передается методу handleEvent(), который должен либо обработать его самостоятельно, либо передать методу handleEvent() класса-предка. Последний уже производит разбор сообщений на элементарные и вызывает соответствующие им обработчики. Метод handleEvent() класса-предка также производит диспетчеризацию вызовов метода action().



Следующий этап нашего эксперимента - проверка пересылки сообщений между элементами пользовательского интерфейса и аплетом. Создадим свой класс кнопки и назовем его MyButton. Добавьте в файл Events.java следующий исходный текст:

class MyButton extends Button { public MyButton(String text) { super(text); }

public boolean mouseDown(Event evt, int x, int y) { System.out.println ("--> MyButton.mouseDown() called..."); return true; }

public boolean mouseUp(Event evt, int x, int y) { System.out.println("--> MyButton.mouseUp() called..."); return true; }

public boolean mouseDrag(Event evt, int x, int y) { System.out.println("--> MyButton.mouseDrag() called..."); return true; }

public boolean mouseMove(Event evt, int x, int y) { System.out.println("--> MyButton.mouseMove() called..."); return true; }

public boolean mouseEnter(Event evt, int x, int y) { System.out.println("--> MyButton.mouseEnter() called..."); return true; }

public boolean mouseExit(Event evt, int x, int y) { System.out.println("--> MyButton.mouseExit() called..."); return true; }

public boolean keyDown(Event evt, int key) { System.out.println("--> MyButton.keyDown() called..."); }

public boolean keyUp(Event evt, int key) { System.out.println("--> MyButton.keyUp() called..."); return true; }

public boolean gotFocus(Event evt, Object o) { System.out.println("--> MyButton.gotFocus() called..."); return true; }

public boolean lostFocus (Event evt, Object o) { System.out.println("--> MyButton.lostFocus() called..."); return true; } }

Замените также в исходном тексте аплета все упоминания класса Button на MyButton. Перекомпилируйте и запустите аплет. Попробуйте поэкспериментировать с нажатиями на кнопку. Как вы заметили, все события стали передаваться непосредственно классу кнопки. Это же происходит и с нажатиями на кнопки клавиатуры.

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

Еще одна странность наблюдается при установленном на кнопке фокусе ввода. Если теперь нажать клавишу <Tab>, то ничего не произойдет. Фокус останется на кнопке и не будет передан на строку ввода, как это ожидается. Секрет прост: наш класс не пропускает нажатие <Tab> далее. Чтобы исправить ситуацию, вставим следующую строку в обработчик keyDown():

return key == 9 ? false : true;

Теперь, если код нажатой клавиши равен коду клавиши <Tab>, т. е. равен 9, то мы не обрабатываем событие, а возвращаем false, передавая его дальше в систему. Вывод: в Java-программах нет различия между функциональными клавишами и обычными. Поэтому вы должны сами отслеживать сомнительные моменты.

На сегодня достаточно. Вам предоставляется возможность самостоятельно поэкспериментировать с готовым аплетом.


Содержание раздела