Microsoft Visual J++. Создание приложений и аплетов на языке Java. Часть 2© Александр Фролов, Григорий ФроловТом 32, М.: Диалог-МИФИ, 1997, 288 стр. Приложение CDRotationЗадача отображения видеофильмов в окне Java настолько важна, что Microsoft включил в Visual J++ специальные средства для создания шаблона исходных текстов аплета с анимацией. Если на третьем шаге системы автоматизированной создания исходных текстов аплетов Java Applet Wizard включить переключатель Yes в поле Would you like your applet to be multi-threaded, а также переключатель Yes в поле Would you like support for animation (рис. 4.3), для вас будут созданы исходные тексты аплета, в окне которого находится изображение земного шара, вращающегося вдоль вертикальной оси.
Рис. 4.3. Включение исходного текста для работы с анимацией в создаваемый аплет Все, что вам остается сделать, это изменить созданные для вас исходные тексты таким образом, чтобы они соответствовали вашим потребностям. Именно так мы создали исходные тексты приложения CDRotation, в окне которого изображается вращающийся компакт-диск. Когда будете запускать приложение CDRotation, обратите внимание, что в левом верхнем углу каждого кадра отображается его порядковый номер. Этот номер не нарисован в файлах кадров, а надписывается приложением после рисования очередного кадра. Такое невозможно, если располагать в документе HTML файл AVI или многосекционный файл GIF. Исходные тексты приложенияГлавный файл исходных текстов приложения CDRotation представлен в листинге 4.7. Листинг 4.7. Файл CDRotation\CDRotation.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 CDRotation extends Applet implements Runnable
{
// Ссылка на задачу рисования
// вращающегося компакт-диска
Thread m_CDRotation = null;
// Контекст отображения для рисования
private Graphics m_Graphics;
// Массив изображений компакт-диска
private Image m_Images[];
// Номер текущего изображения
private int m_nCurrImage;
// Ширина изображения
private int m_nImgWidth = 0;
// Высота изображения
private int m_nImgHeight = 0;
// Флаг загрузки всех изображений
private boolean m_fAllLoaded = false;
// Общее количество изображений
private final int NUM_IMAGES = 11;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: CDRotation\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// displayImage
// Рисование текущего изображения, если все изображения
// уже загружены
// -------------------------------------------------------
private void displayImage(Graphics g)
{
// Если не все изображения загружены,
// ничего не делаем
if (!m_fAllLoaded)
return;
// Рисуем текущее изображение в центре окна аплета
g.drawImage(m_Images[m_nCurrImage],
(size().width - m_nImgWidth) / 2,
(size().height - m_nImgHeight) / 2, null);
// Рисуем в вернем левом углу кадра его порядковый номер
g.drawString((new Integer(m_nCurrImage)).toString(),
(size().width - m_nImgWidth) / 2,
((size().height - m_nImgHeight) / 2) + 10);
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения белый цвет
g.setColor(Color.white);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Если все изображения загружены, рисуем
// текущее изображение
if (m_fAllLoaded)
{
displayImage(g);
}
// Если не загружены, рисуем сообщение
// о загрузке
else
g.drawString("Подождите, идет загрузка...",
10, dimAppWndDimension.height / 2);
}
// -------------------------------------------------------
// start
// Метод вызывается при первом отображении окна аплета
// -------------------------------------------------------
public void start()
{
if (m_CDRotation == null)
{
m_CDRotation = new Thread(this);
m_CDRotation.start();
}
}
// -------------------------------------------------------
// stop
// Метод вызывается, когда окно аплета исчезает с экрана
// -------------------------------------------------------
public void stop()
{
if (m_CDRotation != null)
{
m_CDRotation.stop();
m_CDRotation = null;
}
}
// -------------------------------------------------------
// run
// Метод, который работает в рамках отдельной задачи
// Он рисует в окне аплета изображения - кадры
// клипа "вращающийся компакт-диск"
// -------------------------------------------------------
public void run()
{
// Инициализируем номер текущего изображения
m_nCurrImage = 0;
// Проверяем, все ли изображения загружены.
// Если нет, загружаем их
if (!m_fAllLoaded)
{
// Перерисовываем окно аплета
repaint();
// Получаем контекст отображения для окна
m_Graphics = getGraphics();
// Создаем массив изображений
m_Images = new Image[NUM_IMAGES];
// Создаем объект MediaTracker для контроля
// загружки изображений
MediaTracker tracker = new MediaTracker(this);
// Переменная для хранения имени файла изображения
String strImage;
// Цикл загрузки изображений
for (int i = 0; i < NUM_IMAGES; i++)
{
// Записываем в строку strImage имя текущего файла
// с изображением
strImage = "images/cdimg0" +
((i < 10) ? "0" : "") + i + ".gif";
// Инициируем получение изображения
m_Images[i] = getImage(getDocumentBase(), strImage);
// Добавляем изображение в объект MediaTracker
tracker.addImage(m_Images[i], 0);
}
// Ожидаем окончание загрузки всех изображений
try
{
tracker.waitForAll();
// Если не было ошибок, устанавливаем флаг
// окончания загрузки
m_fAllLoaded = !tracker.isErrorAny();
}
catch (InterruptedException e)
{
}
// Если при загрузке изображений произошла ошибка,
// останавливаем задачу и рисуем сообщение об
// ошибке
if (!m_fAllLoaded)
{
stop();
m_Graphics.drawString(
"При загрузке изображений произошла ошибка",
10, size().height / 2);
return;
}
// Сохраняем ширину и высоту первого изображения
m_nImgWidth = m_Images[0].getWidth(this);
m_nImgHeight = m_Images[0].getHeight(this);
}
// Перерисовываем окно аплета
repaint();
// Запускаем цикл рисования изображений
while (true)
{
try
{
// Рисуем текущее изображение
displayImage(m_Graphics);
// Увеличиваем номер текущего изображения
m_nCurrImage++;
// Если достигли максимального номера,
// начинаем с самого начала
if(m_nCurrImage == NUM_IMAGES)
m_nCurrImage = 0;
// Выполняем задержку в 30 миллисекунд
Thread.sleep(30);
}
// Если в процессе рисования возникло
// исключение, останавливаем задачу
catch (InterruptedException e)
{
stop();
}
}
}
}
Листинг 4.8 содержит исходный текст документа HTML, созданного для аплета CDRotation. Листинг 4.8. Файл CDRotation\CDRotation.html
<html>
<head>
<title>CDRotation</title>
</head>
<body>
<hr>
<applet
code=CDRotation.class
id=CDRotation
width=320
height=240 >
</applet>
<hr>
<a href="CDRotation.java">The source.</a>
</body>
</html>
Описание исходных текстовРассмотрим наиболее важные методы нашего приложения. Метод startВ задачу метода start, который получает управление при отображении окна аплета, входит создание и запуск задачи, отображающий кадры видеофильма с изображением вращающегося компакт-диска:
if (m_CDRotation == null)
{
m_CDRotation = new Thread(this);
m_CDRotation.start();
}
Задача создается как объект класса Thread, причем конструктору передается ссылка на главный класс аплета. Поэтому при запуске задачи управление получит метод run, определенный в классе аплета. Метод stopМетод stop останавливает работу задачи, когда окно аплета исчезает с экрана:
if(m_CDRotation != null)
{
m_CDRotation.stop();
m_CDRotation = null;
}
Для остановки вызывается метод stop. Метод paintСразу после получения управления, метод paint закрашивает окно аплета белым цветом и рисует вокруг него черную рамку. Затем метод проверяет содержимое флага m_fAllLoaded. Этот флаг установлен в значение true, когда все кадры видеофильма загружены и сброшен в значение false, когда загрузка кадров еще не завершена. Последняя ситуация возникает всегда при первом вызове метода paint. Если все изображения загружены, метод paint вызывает метод displayImage, определенный в нашем приложении:
if(m_fAllLoaded)
{
displayImage(g);
}
Этот метод, о котором мы еще расскажем подробнее, отображает в окне аплета текущий кадр видеофильма. Если же кадры видеофильма еще не загружены, в окне аплета отображается соответствующее сообщение:
else
g.drawString("Подождите, идет загрузка...",
10, dimAppWndDimension.height / 2);
Метод runМетод run работает в рамках отдельной задачи. Он занимается последовательным рисованием кадров нашего видеофильма. Прежде всего метод run записывает нулевое значение в поле m_nCurrImage, хранящее номер текущего отображаемого кадра: m_nCurrImage = 0; Далее выполняется проверка, загружены ли все кадры видеофильма, для чего анализируется содержимое флага m_fAllLoaded. Если изображения не загружены (а в самом начале так оно и есть) метод run перерисовывает окно аплета и получает контекст отображения для этого окна. Затем создается массив объектов Image для хранения кадров видеофильма: m_Images = new Image[NUM_IMAGES]; Метод run создает также объект класса MediaTracker для ожидания загрузки всех кадров видеофильма: MediaTracker tracker = new MediaTracker(this); Далее метод run в цикле загружает изображения и добавляет их в объект класса MediaTracker для того чтобы можно было дождаться загрузки всех кадров:
for (int i = 0; i < NUM_IMAGES; i++)
{
strImage = "images/cdimg0" + ((i < 10) ? "0" : "") +
i + ".gif";
m_Images[i] = getImage(getDocumentBase(), strImage);
tracker.addImage(m_Images[i], 0);
}
Здесь предполагается, что файлы изображений находятся в каталоге images, который, в свою очередь, размещен там же, где и двоичный файл аплета. Имена файлов, составляющих отдельные кадры, начинаются с префикса cdimg0, вслед за которым идет номер кадра (00, 01, 02, и так далее), и расширение имени .gif. Ожидание загрузки кадров выполняется с помощью метода waitForAll, о котором мы вам уже рассказывали:
try
{
tracker.waitForAll();
m_fAllLoaded = !tracker.isErrorAny();
}
catch (InterruptedException e)
{
}
После окончания ожидания флаг завершения загрузки устанавливается только в том случае, если метод isErrorAny вернул значение false, то есть если не было никаких ошибок. Если же произошла ошибка, в окне аплета отображается соответствующее сообщение, после чего работа метода run (и, следовательно, работа созданной для него задачи) заканчивается:
if(!m_fAllLoaded)
{
stop();
m_Graphics.drawString(
"При загрузке изображений произошла ошибка",
10, size().height / 2);
return;
}
В случае удачной загрузки всех кадров метод run получает ширину и высоту первого кадра видеофильма и сохраняет эти значения в переменных m_nImgWidth и m_nImgHeight: m_nImgWidth = m_Images[0].getWidth(this); m_nImgHeight = m_Images[0].getHeight(this); Далее окно аплета перерисовывается: repaint(); При этом метод paint отображает в окне аплета первый кадр видеофильма. На следующем этапе работы метода run запускается цикл отображения кадров фильма:
while (true)
{
try
{
displayImage(m_Graphics);
m_nCurrImage++;
if(m_nCurrImage == NUM_IMAGES)
m_nCurrImage = 0;
Thread.sleep(30);
}
catch (InterruptedException e)
{
stop();
}
}
В этом бесконечном цикле вызывается метод displayImage, рисующий текущий кадр видеофильма, после чего номер текущего кадра увеличивается на единицу. Если показаны все кадры, номер текущего кадра становится равным нулю, а затем процесс продолжается. Между отображением кадров выполняется задержка величиной 30 миллисекунд. Метод displayImageМетод displayImage вызывается из двух мест - из метода paint при перерисовке окна аплета и из метода run (периодически). Если кадры видеофильма не загружены, содержимое флага m_fAllLoaded равно false и метод displayImage просто возвращает управление, ничего не делая: if(!m_fAllLoaded) return; Если же загрузка изображений завершена, этот метод рисует в центре окна текущий кадр видеофильма, вызывая для этого знакомый вам метод drawImage: g.drawImage(m_Images[m_nCurrImage], (size().width - m_nImgWidth) / 2, (size().height - m_nImgHeight) / 2, null); После того как кадр нарисован, мы надписываем на нем его порядковый номер, вызывая для этого метод drawString: g.drawString((new Integer(m_nCurrImage)).toString(), (size().width - m_nImgWidth) / 2, ((size().height - m_nImgHeight) / 2) + 10); |


