Рассмотрим алгоритм отрисовки отдельного компонента, что определяет его внешний вид? Для этой задачи предназначен метод paint. Этот метод вызывается каждый раз, когда необходимо отобразить компонент на экране.
него есть один аргумент, тип которого – абстрактный класс Graphics. В этом классе определено множество методов для отрисовки простейших графических элементов – линий, прямоугольников и многоугольников, окружностей и овалов, текста, картинок и т.д.
Наследники класса Component переопределяют метод paint и, пользуясь методами Graphics, задают алгоритм прорисовки своего внешнего вида:
public void paint(Graphics g) { g.drawLine(0, 0, getWidth(), getHeight()); g.drawLine(0, getHeight(), getWidth(), 0);
}
Ключевым классом при выполнении всех графических операций является Graphics. Назначение класса:
определяет поверхность рисования; определяет методы рисования;
определяет атрибуты для методов рисования.
Рассмотрим некоторые методы класса Graphics.
drawLine(x1, y1, x2, y2)
Этот метод отображает линию толщиной в 1 пиксел, проходящую из точки (x1, y1) в (x2, y2).
drawRect(int x, int y, int width, int height)
Этот метод отображает прямоугольник, чей левый верхний угол находится в точке (x, y), а ширина и высота равняются width и height
соответственно. Правая сторона пройдет по линии x+width, а нижняя – y+height.
fillRect(int x, int y, int width, int height)
Этот метод закрашивает прямоугольник. Левая и правая стороны прямоугольника проходят по линиям x и x+width-1 соответственно, а верхняя нижняя – y и y+height-1 соответственно. Таким образом, чтобы зарисовать все пикселы компонента, необходимо передать следующие аргументы:
g.fillRect(0, 0, getWidth(), getHeight());
drawOval(int x, int y, int width, int height)
Этот метод рисует овал, вписанный в прямоугольник, задаваемый указанными параметрами. Очевидно, что если прямоугольник имеет равные стороны (т.е. является квадратом), овал становится окружностью.
fillOval(int x, int y, int width, int height)
Этот метод закрашивает указанный овал.
drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
Этот метод рисует дугу – часть овала, задаваемого первыми четырьмя параметрами. Дуга начинается с угла startAngle и имеет угловой размер arcAngle. Начальный угол соответствует направлению часовой стрелки, указывающей на 3 часа. Угловой размер отсчитывается против часовой стрелки. Таким образом, размер в 90 градусов соответствует дуге в четверть овала (верхнюю правую). Углы "растянуты" в соответствии с размером прямоугольника. В результате, например, угловой размер в 45 градусов всегда задает границу дуги по линии, проходящей из центра прямоугольника в его правый верхний угол.
fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
Этот метод закрашивает сектор, ограниченный дугой, задаваемой параметрами.
drawString(String text, int x, int y)
Этот метод выводит на экран текст, задаваемый первым параметром. Точка (x, y) задает позицию самого левого символа. Для наглядности приведем пример:
g.drawString("abcdefgh", 15, 15);
g.drawLine(15, 15, 115, 15);
Методы repaint и update
Кроме paint в классе Component объявлены еще два метода, отвечающие за прорисовку компонента. Как было рассмотрено, вызов paint инициируется операционной системой, если возникает необходимость перерисовать окно приложения, или часть его. Однако может потребоваться обновить внешний вид, руководствуясь программной логикой. Например, отобразить результат операции вычисления, или работы с сетью. Можно изменить состояние компонента (значение его полей), но операционная система не отследит такое изменение и не инициирует перерисовку.
Для программной инициализации перерисовки компонента служит метод repaint. Конечно, у него нет аргумента типа Graphics, поскольку программист не должен создавать экземпляры этого класса (точнее, его наследников, ведь Graphics – абстрактный класс). Метод repaint можно вызывать без аргументов. В этом случае компонент будет перерисован максимально быстро. Можно указать аргумент типа long – количество миллисекунд. Система инициализирует перерисовку спустя указанное время. Можно указать четыре числа типа int (x, y, width, height), задавая прямоугольную область компонента, которая нуждается в перерисовке. Наконец, можно указать все 5 параметров – и задержку по времени, и область перерисовки.
Если перерисовка инициируется приложением, то система вызывает не метод paint, а метод update. У него уже есть аргумент типа Graphics и по умолчанию он лишь закрашивает всю область компонента фоновым цветом
(свойство background), а затем вызывает метод paint. Зачем же было вводить этот дополнительный метод, если можно было сразу вызвать paint? Дело в том, что поскольку перерисовка инициируется приложением, для сложных компонентов становится возможной некоторая оптимизация обновления внешнего вида. Например, если изменение заключается в появлении нового графического элемента, то можно избежать повторной перерисовки остальных элементов – переопределить метод update и реализовать в нем отображение одного только нового элемента. Если же компонент имеет простую структуру, можно оставить метод update без изменений.
Апплеты
Кроме приложений, язык Java позволяет создавать апплеты (applets). Это программы, работающие в среде другой программы — браузера. Апплеты не нуждаются в окне верхнего уровня — им служит окно браузера. Они не запускаются JVM — их загружает браузер, который сам запускает JVM для выполнения апплета. Эти особенности отражаются на написании программы апплета [2].
С точки зрения языка Java, апплет — это всякое расширение класса Applet, который, в свою очередь, расширяет класс panel. Таким образом, апплет — это панель специального вида, контейнер для размещения компонентов с дополнительными свойствами и методами. Менеджером размещения компонентов по умолчанию, как и в классе Panel, служит FiowLayout. Класс Applet находится в пакете java. applet, в котором кроме него есть только три интерфейса, реализованные в браузере. Надо заметить, что не все браузеры реализуют эти интерфейсы полностью.
Поскольку JVM не запускает апплет, отпадает необходимость в методе main (), его нет в апплетах. В апплетах редко встречается конструктор. Дело в том, что при запуске первого создается его контекст. Во время выполнения
конструктора контекст еще не сформирован, поэтому не все начальные значения удается определить в конструкторе.
Начальные действия, обычно выполняемые в конструкторе и методе main(), в апплете записываются в метод init() класса Applet. Этот метод автоматачески запускается исполняющей системой Java браузера сразу же после загрузки апплета. Вот как он выглядит в исходном коде класса Applet:
public void init(){}
Метод init () не имеет аргументов, не возвращает значения и должен переопределяться в каждом апплете — подклассе класса Applet. Обратные действия — завершение работы, освобождение ресурсов — записываются при необходимости в метод destroy о, тоже выполняющийся автоматически при выгрузке апплета. В классе Applet есть пустая реализация этого метода.
Кроме методов init() и destroy() в классе Applet присутствуют еще два пустых метода, выполняющихся автоматически. Браузер должен обращаться к методу start() при каждом появлении апплета на экране и обращаться к методу stop(), когда апплет уходит с экрана. В методе stop() можно определить действия, приостанавливающие работу апплета, в методе start() — возобновляющие ее. Надо сразу же заметить, что не все браузеры обращаются к этим методам как должно [2].
Метод paint(Graphics g) вызывается каждый раз при повреждении апплета. AWT следит за состоянием окон в системе и замечает такие случаи, как, например, перекрытие окна апплета другим окном. В таких случаях, после того, как апплет снова оказывается видимым, для восстановления его изображения вызывается метод paint(Graphics g).
Перерисовка содержимого апплета выполняется методом update(). Для инициации update() предусмотрены несколько вариантов метода repaint, который в свою очередь вызывает метод update: repaint();
repaint(time);
repaint(x, y, w, h);
repaint(time, x, y, w, h);
Приведем пример простого апплета, выводящего текст на экран.
import java.awt.*;
import java.applet.*;
public class HelloWorld extends Applet{
public void paint(Graphics g){
g.drawstring("Hello, XXI century World!!!", 10, 30);
}}
Эта программа записывается в файл HelloWorld.java и компилируется как обычно: javac HelloWorld.java.
Компилятор создает файл HelloWorkLclass, но воспользоваться для его выполнения интерпретатором java теперь нельзя — нет метода main(). Вместо интерпретации надо дать указание браузеру для запуска апплета.
Все указания браузеру даются пометками, тегами (tags), на языке HTML (HyperText Markup Language). В частности, указание на запуск апплета дается в теге <applet>. В нем обязательно задается имя файла с классом апплета параметром code, ширина width и высота height панели апплета в пикселах. Полностью текст HTML для запуска нашего апплета на странице браузера приведен ниже:
<html>
<head><title> Applet</title></head><body> Ниже выполняется апплет.<br>
<applet code = "HeiioWorid.class" width = "200" height = "100">
</applet>
</body>
</html>
Этот текст заносится в файл с расширением html или htm, например. HelloWorld.html. Имя файла произвольно, никак не связано с апплетом или классом апплета. Оба файла — HelloWorld.html и HelloWorld.class — помещаются в один каталог на сервере, и файл HelloWorld.html загружается в
браузер, который может находиться в любом месте Internet. Браузер, просматривая HTML-файл, выполнит тег <appiet> и загрузит апплет.
В этом простом примере можно заметить еще две особенности апплетов. Во-первых, размер апплета задается не в нем, а в теге <applet>. Это очень удобно, можно менять размер апплета, не компилируя его заново. Можно организовать апплет невидимым, сделав его размером в один пиксел. Кроме того, размер апплета разрешается задать в процентах по отношению к размеру окна браузера, например,
<applet code = "HelloWorld.class" width = "100%" height = "100%">
Во-вторых, как видно на рисунке, у апплета серый фон. Такой фон был в первых браузерах, и апплет не выделялся из текста в окне браузера. Теперь в браузерах принят белый фон, его можно установить обычным для компонентов методом setBackground(Color.white), обратившись к нему в методе init ().
В настоящее время синтаксис тега <APPLET> таков [1]:
<APPLET
CODE = appletFile
WIDTH = pixels
HEIGHT = pixels
[ARCHIVE = jarFiles]
[CODEBASE = codebaseURL]
[ALT = alternateText]
[NAME = appletInstanceName]
[ALIGN = alignment]
[VSPACE = pixels]
[HSPACE = pixels]
>
[HTML-текст, отображаемый при отсутствии
поддержки Java]
</APPLET>
CODE = appletClassFile; CODE – обязательный атрибут, задающий имя файла, в котором содержится описание класса апплета. Имя файла задается относительно codebase, то есть либо от текущего каталога, либо от каталога, указанного в атрибуте CODEBASE.
WIDTH = pixels
HEIGHT = pixels; WIDTH и HEIGHT - обязательные атрибуты,
задающие размер области апплета на HTML -странице.
ARCHIVE = jarFiles; Этот необязательный атрибут задает список jar -файлов (разделяется запятыми), которые предварительно загружаются в Web -браузер. В них могут содержаться классы, изображения, звук и любые другие ресурсы, необходимые апплету. Архивирование наиболее необходимо именно апплетам, так как их код и ресурсы передаются через сеть.
CODEBASE = codebaseURL; CODEBASE – необязательный атрибут, задающий базовый URL кода апплета; является каталогом, в котором будет выполняться поиск исполняемого файла апплета (задаваемого в признаке CODE). Если этот атрибут не задан, по умолчанию используется каталог данного HTML -документа. С помощью этого атрибута можно на странице одного сайта разместить апплет, находящийся на другом сайте.
ALT = alternateAppletText; Признак ALT – необязательный атрибут, задающий короткое текстовое сообщение, которое должно быть выведено (как правило, в виде всплывающей подсказки при нахождении курсора мыши над областью апплета) в том случае, если используемый браузер распознает синтаксис тега <applet>, но выполнять апплеты не умеет. Это не то же самое, что HTML -текст, который можно вставлять между <applet> и </applet> для браузеров, вообще не поддерживающих апплетов.
NAME = appletInstanceName; NAME – необязательный атрибут,
используемый для присвоения имени данному экземпляру апплета. Имена апплетам нужны для того, чтобы другие апплеты на этой же странице могли находить их и общаться с ними, а также для обращений из Java Script.
ALIGN = alignment VSPACE = pixels
HSPACE = pixels; Эти три необязательных атрибута предназначены для того же, что и в теге IMG. ALIGN задает стиль выравнивания апплета, возможные значения: LEFT, RIGHT, TOP, TEXTTOP, MIDDLE, ABSMIDDLE, BASELINE, BOTTOM, ABSBOTTOM.
В состав JDK любой версии входит программа appletviewer. Это простейший браузер, предназначенный для запуска апплетов в целях отладки. Если под рукой нет Internet-браузера, можно воспользоваться им. Appletviewer запускается из командной строки:
appletviewer HelloWorld.html
Приведем пример невидимого апплета. В нижней строке браузера— строке состояния (status bar) — отражаются сведения о загрузке файлов.
Апплет может записать в нее любую строку str методом showstatus(string str).
Файл RunningString.Java import java.awt.*; import java.applet.*;
public class Runningstring extends Applet{ private Boolean go;
public void start(){ go = true;
sendMessage("Эта строка выводится апплетом"); }
public void sendMessage(String s){
String s1 = s+"";
while(go){
showStatus(s);
try{
Thread.sleep(200);
}catch(Exception e){}
s = s1.substring(1)+s.charAt(0);
s1 =s; }
}
public void stop(){
go = false; } }
(Running.zip)
Следующий апплет имеет более сложную структуру: по экрану движется круг, который упруго отражается от границ области. Движение происходит непрерывно. Особенность данного апплета состоит в том, что он реализован помощью дополнительного потока Thread, который отвечает за движение круга. Класс апплета реализует интерфейс Runnable. Программный код приведен ниже (ссылка на файл bouncingcircle.java).
import java.applet.*;
import java.awt.*;
//Создаем собственный класс,который наследуется от
//класса Applet
//Данный класс реализует методы интерфейса Runnable
public class BouncingCircle extends Applet implements Runnable { int x = 150, y = 50, r = 50; // Центр и радиус круга
int dx = 11, dy = 7; // Движение круга по горизонт и вертик Thread animator; // Нить, которая осуществляет анимацию
volatile boolean pleaseStop; // Флаг остановки движения
//Метод для рисования окружности красным цветом
public void paint(Graphics g) {
g.setColor(Color.red); //установка цвета для g g.fillOval(x-r, y-r, r*2, r*2); //прорисовка круга
}
//Метод двигает круг и "отражает" его при ударе круга
//о стенку, затем вызывает перерисовку.
//Данный метод вызывается многократно анимационным потоком
public void animate() {
Rectangle bounds = getBounds();
//Получение размера окна программы
if ((x - r + dx < 0) || (x + r + dx > bounds.width)) dx = -dx; if ((y - r + dy < 0) || (y + r + dy > bounds.height)) dy = -dy;
//Изменение координат круга, по сути - движение.
x += dx; y += dy;
//"Просим" браузер вызвать метод paint() для отрисовки круга в новой позиции
repaint();
}
/*Это метод из интерфейса Runnable. Это тело потока исполнения, осуществляющего анимацию. Сам поток создается и запускается методом start()*/
public void run() {
while(!pleaseStop) {// Цикл до тех пор, пока не будет
//команды остановиться.
animate();// Обновляем положение и перерисовываем
try { Thread.sleep(100); }// Ждем 100 миллисекунд
catch(InterruptedException e) {} // Игнорируем прерывания
}
}
//Запускаем анимацию при запуске апплета браузером
public void start() {
animator = new Thread(this); // Создаем поток исполнения
pleaseStop = false;// Пока не просим его остановиться
}//Останавливаем анимацию, когда браузер останавливает апплет public void stop() { pleaseStop = true; }
}
Данный апплет работает следующим образом:
(BouncingCircle)