Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

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);
[Назад] [Содеожание] [Дальше]