Главная » Java, Web » Потоки. Анимация. Таймер

0

Java позволяет работать с потоками, при этом несколько независимых потоков могут работать одновременно друг с другом. Поток — базовая единица при работе программы. Поток выполняет последовательность инструкций, причем делает это последовательно, выбирая одну инструкцию за другой. Поток прекращается, если прекращается выполнение программы в целом. В каждый конкретный момент только один поток будет выполняться, поскольку центральный процессор может выполнять только одно задание в отдельный момент. Исключениями являются многопроцессорные системы, состоящие из нескольких процессоров. Компьютер использует разделение времени, что дает иллюзию одновременности выполнения нескольких потоков в одно и то же время. Процессор какое-то время выполняет задачу одного потока, затем переходит к другому и так далее и, наконец, возвращается снова к первому потоку, и цикл повторяется на новом "витке". Такой цикл может быть очень кратковременным, например, в одной секунде может уложиться около сотни таких циклов. Для разработчика и для пользователя создается полная иллюзия того, что потоки выполняются параллельно и одновременно. Многопоточность языка Java означает, что программа, созданная на этом языке, может создавать один или несколько потоков, которые затем выполняются параллельно вместе с основной программой. Многопоточность является фундаментальным понятием в Java. Использование потоков не всегда является простой задачей, поэтому потоки следует использовать только тогда, когда в них действительно есть необходимость.

Анимация и Swing

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

В GUI-программе существует по крайней мере один поток, задача которого — отслеживание и обработка событий, которые производит пользователь. Анимация никак не связана с этими событиями, она представляет собой программу, которая выполняется естественным путем без вмешательства извне. Наиболее естественным для этого будет создать независимый поток. До разработки и создания пакета swing такой поток необходимо было создавать в явном виде. Как следствие этого работа с анимацией представляла гораздо более сложную задачу, чем следовало бы. С использованием swing работа с простой анимацией значительно упростилась. Сейчас нет необходимости работать с потоками непосредственно, все это уже сделано в пакете Swing. Идея состояла в том, чтобы объединить в пакете swing обработку событий так, чтобы работа с анимацией выглядела так же, как и задача по созданию других приложений на основе графического интерфейса пользователя.

Анимация может быть создана с использованем объектов, принадлежащих классу javax.swing.Timer. Объект Timer может генерировать последовательность событий без вмешательства пользователя. При работе с анимацией необходимо создать объект Timer и обрабатывать каждое сообщение таймера, отображая один кадр анимации за другим. При этом в фоне таймер работает в виде самостоятельного потока, но нам нет необходимости работать с этим поток в явном виде.

События, которые генерируются таймером, — это события типа ActionEvent. Конструктор таймера в качестве параметров получает два значения — промежуток времени между двумя событиями и прослушиватель событий ActionListener, который будет уведомляться о наступлении события:

Timer(int delayTime, ActionListener listener);

Прослушиватель событий должен отвечать на возникающие события, для этого используется метод actionPerformed (). Интервал между событиями указывается в миллисекундах (в одной секунде 1000 мс). Фактически этот интервал может оказаться несколько большим, что зависит от времени, которое может потребоваться для обработки события, и степени загруженности компьютера задачами. В целях воспроизведения анимации интервал устанавливается продолжительностью от 10 до 30 фреймов в секунду, то есть от 100 до 33 мс.

Таймер не начинает свою работу автоматически сразу после того, как он будет создан. Чтобы запустить таймер, необходимо воспользоваться методом start (). Для остановки таймера используется метод stop (). Этот метод вызывается для того, чтобы остановить процесс генерации событий таймера. Если таймер был остановлен, то для возобновления работы таймера используется метод restart (). Метод start () должен быть вызван только один раз. Все перечисленные методы не имеют параметров.

В следующем апплете (листинг 1.32) мы запускаем анимацию, нажимая кнопку Start. После нажатия кнопки надпись на ней изменяется, мы увидим новую надпись Stop. Нажатие этой кнопки в дальнейшем приведет к остановке анимации. Этот апплет "говорит": "Hello, World!". Анимация используется для постепенного изменения цвета выводимого текста (рис. 1.25).

Рис. 1.25. Апплет с анимацией

Листинг 1.32. Файл HelloWorldSpectrum.java

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

public class HelloWorldSpectrum extends JApplet {

Display display; // JPanel вложенного класса "Display"

// для отображения "Hello, World!" JButton startStopButton; // кнопка, которая используется

// для запуска и остановки анимации Timer timer;      // таймер, управляющий анимацией

// Таймер запускается, когда пользователь нажимает // кнопку. Каждый раз при получении события таймера // изменяется цвет выводимого текста. // Значение переменной равно null, // если анимация не работает.

int colorlndex; 11 Число в промежутке от 0 до 100, // которое определяет цвет. // При получении события это число // увеличивается на 1. public void init() { // вызывается системой для инициализации апплета display = new Display(); // компонент, отображающий "Hello, World!" getContentPane().add(display, BorderLayout.CENTER); // вставляет панель отображения в центр // панели апплета JApplet JPanel buttonBar = new JPanel();

// панель, содержащая кнопку buttonBar.setBackground(Color.gray); getContentPane().add(buttonBar, BorderLayout.SOUTH); startStopButton = new JButton("Start"); buttonBar.add(startStopButton);

startStopButton.addActionListener(new ActionListener() { // Прослушиватель событий, который реагирует // на нажатия кнопки, запускающей и // останавливающей анимацию //и проверяющей значение таймера. // Если таймер не null, то анимация запущена, // то есть ее надо остановить. Если таймер null, то // анимацию следует запустить. public void actionPerformed(ActionEvent evt) { if (timer == null)

startAnimation(); else

stopAnimation() ;

}

}) ;

}

ActionListener timerListener = new ActionListener() { // Описывает прослушиватель событий, отвечающий //на события таймера.

// При получении событий таймера индекс цвета изменяется на 1. public void actionPerformed(ActionEvent evt) { colorIndex++; // число от 0 до 100 if (colorlndex > 100) colorlndex = 0;

float hue = colorlndex / 100.OF; 11 От 0.0F до 1.0F.

display.setColor(Color.getHSBColor(hue,1,1)); }

} ;

void startAnimation() {

// Запускается анимация, если она еще не запущена. // Состояние проверяется по значению таймера. // Если значение null, то анимация не запущена, if (timer == null) { // создается таймер, который будет // создавать событие каждые 50 мс, отправляя их / / прослушивателю событий timer = new Timer(50, timerListener); timer.start(); // Запускаем таймер.

startStopButton.setText("Stop"); }

}

void stopAnimation() { if (timer != null) { timer.stop(); // останавливаем таймер timer = null; // переменной таймера присваиваем null

startStopButton.setText("Start") ; }

}

public void stop() {

// Метод апплета stop() вызывается системой перед тем, // как апплет должен прекратить работу временно или навсегда. // Работающий таймер не нужен тогда, когда апплет стоит, // останавливаем анимацию. Если анимация не работает, // то ее не надо останавливать.

stopAnimation() ; }

// вложенный класс для представления "холста" для рисования class Display extends JPanel {

// Вложенный класс представляет панель для отображения // строки "Hello, World!". Цвет и шрифт запоминаются //в переменных colorNum и textFont.

Color color; 11 цвет, которым выводится текст (изначально красный) Font textFont; // шрифт, которым выводится текст

// объект шрифта содержит размер и стиль текста

Display () {

// Конструктор класса Display. Задается цвет фона и начальные // значения для переменных цвета и шрифта. setBackground(Color.black);

color = Color.red; // начальный цвет текста textFont = new Font("Serif",Font.BOLD,36); // создается объект шрифта

}

void setColor(Color color) {

// Метод вызывается из основного класса для установки цвета // текста и меняет цвет текста. // Цвет Color не должен быть null, this.color = color; repaint();

}

public void paintcomponent(Graphics g){ // Вызывается системой для прорисовки панели Jpanei. // Вызывается класс super.paintcomponent() // для заполнения фона заданным цветом.

// Затем выводится текст в соответствии с текущим цветом, super.paintcomponent(g); g.setColor(color);

g.setFont(textFont); // задается шрифт

g.drawstring("Hello World!", 25,50); // выводится текст

}

}

}

Апплет реагирует на события ActionFvents, которые генерируются либо кнопкой, либо таймером. Для каждого источника используется отдельный прослушиватель событий. В апплете заданы методы startAnimation () и stopAnimation (), эти методы вызываются при нажатии на кнопку. Каждый раз при нажатии на кнопку Start создается новый таймер:

timer = new Timer(50,timerListener); timer.start ();

Методы start() и stop() в апплете JApplet

Некоторые методы требуют дополнительных объяснений. Зачем используется метод stop () ? Каждый апплет имеет набор методов, которые выполняются в тот или иной момент жизненного цикла апплета. Так, метод init о вызывается при создании апплета перед тем, как он будет отображен на экране. Метод destroy о непосредственно перед тем, как апплет будет уничтожен, чтобы дать шанс для высвобождения ресурсов. Методы start () и stop() вызываются между методами inito и destroy (). Метод start () вызывается сразу после метода inito, а метод stop о вызывается непосредственно перед методом destroy(). Методы start () и stop() можно вызывать и в других ситуациях. Причиной этого является тот факт, что апплет не всегда бывает активен. Предположим, что мы переходим на новую страницу, покидая ту, на которой расположен апплет. Апплет перестает быть видимым, в нем не выполняется метод прорисовки, апплет не получает никаких событий от пользователя. Система вызывает метод stop() тогда, когда мы покидаем страницу, на которой расположен апплет. При возвращении на страницу будет вызван метод start о, что делает апплет вновь активным. Это полезно потому, что апплет не будет использовать ресурсы системы, когда находится в неактивном состоянии.

В нашем случае вряд ли следует оставлять таймер работающим в течение того времени, когда апплет будет неактивен. В методе stop о происходит обращение к методу stopAnimation (), который выключает таймер (листинг 1.33).

Листинг 1.33. Файл ScrollingHelloWorld.java

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

public class ScrollingHelloWorld extends JApplet implements ActionListener { Timer timer; // Таймер, на основе которого создана анимация.

//Он создается и запускается в методе start() //и останавливается в методе апплета stop(). String message = "Hello World (for absolutely the last time)!";

// текст, который прокручивается на экране int messagePosition = -1;

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

11 Значение -1 соответствует тому, // что прокрутка еще не началась.

int messageHeight; // данные о размере текстового сообщения int messageWidth;

int charWidth; // ширина одного символа public void init() {

// Панель JPanel используется в качестве "холста" // для рисования апплета, здесь задаются цвета и шрифт. // Используется равноширинный шрифт "Monospaced", // все символы имеют одинаковую ширину. JPanel display = new JPanel() { public void paintComponent(Graphics g) { // отображение текста с позиции // messagePosition в пикселах super.paintComponent(g);

g. drawstring (message, getwidth() — messagePosition, getHeight()/2 + messageHeight/2);

}

} ;

setContentPane(display); display.setBackground(Color.white); display.setForeground(Color.red);

Font messageFont = new Font("Monospaced", Font.BOLD, 30);

display.setFont(messageFont);

FontMetrics fm = getFontMetrics(messageFont);

messageWidth = fm.stringWidth(message);

messageHeight = fm.getAscent();

charWidth = fm.charWidth(‘H’) ; }

public void start() {

// Вызывается при первом и повторном запусках апплета // Создает новый таймер или запускает существующий таймер. if (timer == null) { timer = new Timer(300, this); timer.start();

}

else { timer.restart() ;

}

public void stop() {

// Вызывается тогда, когда апплет должен быть остановлен. // Останавливает таймер.

timer.stop(); }

public void actionPerformed(ActionEvent evt) { // Вычисляет и отображает следующий фрейм анимации. // Метод вызывается в ответ на событие таймера. // Передвигает текст на один символ влево. // Если текст полностью исчезает из апплета, // все возвращается в начальное состояние. messagePosition += charWidth;

if (getSize().width — messagePosition + messageWidth < 0) { // текст перемещается

messagePosition = 0; }

repaint(); }

Апплет показывает прокручивающийся текст (рис. 1.26, 1.27).

Рис. 1.26. Анимированный текст (начало движения)

Рис. 1.27. Анимированный текст (продолжение движения)

Прочие методы использования таймера

Помимо генерации последовательности равноудаленных во времени событий, таймер может быть использован для однократного создания события. Для этого необходимо после создания нового объекта таймера setRepeats передать ему в качестве параметра значение false. Например:

Timer alarm = new Timer(5000, listener); alarm.setRepeats(false); alarm.start();

Такой способ использования таймера создает своеобразные "звоночки". В нашем случае "звонок" прозвенит один раз по истечении 5000 миллисекунд (5 секунд). Звонок можно отменить, для этого необходимо использовать метод stop () объекта Timer в любое время до наступления события (до звонка).

После создания таймера мы имеем возможность изменить начальное значение времени, отделяющее события друг от друга в повторяющемся таймере, ДЛЯ этого необходимо вызвать метод timer. setlnitialDelay (delay) .

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

По теме:

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